]> git.lizzy.rs Git - dragonblocks_alpha.git/commitdiff
refactoring
authorElias Fleckenstein <eliasfleckenstein@web.de>
Fri, 15 Apr 2022 21:45:01 +0000 (23:45 +0200)
committerElias Fleckenstein <eliasfleckenstein@web.de>
Fri, 15 Apr 2022 21:45:01 +0000 (23:45 +0200)
153 files changed:
.gitignore
.gitmodules
README.md
deps/asprintf [new submodule]
deps/dragonnet
deps/dragonport [deleted submodule]
deps/dragonstd
deps/dragontype [deleted submodule]
deps/protogen [new submodule]
models/player.txt [new file with mode: 0644]
schematics/spawn_hut.gox [new file with mode: 0644]
schematics/spawn_hut.txt [new file with mode: 0644]
shaders/3d/fragment.glsl [deleted file]
shaders/3d/vertex.glsl [deleted file]
shaders/terrain/fragment.glsl [new file with mode: 0755]
shaders/terrain/vertex.glsl [new file with mode: 0755]
singleplayer.sh
snapshot.sh
src/CMakeLists.txt
src/client/blockmesh.c [deleted file]
src/client/blockmesh.h [deleted file]
src/client/camera.c
src/client/camera.h
src/client/client.c
src/client/client.h
src/client/client_auth.c
src/client/client_auth.h
src/client/client_config.c
src/client/client_config.h
src/client/client_entity.c [new file with mode: 0644]
src/client/client_entity.h [new file with mode: 0644]
src/client/client_map.c [deleted file]
src/client/client_map.h [deleted file]
src/client/client_node.c
src/client/client_node.h
src/client/client_player.c
src/client/client_player.h
src/client/client_terrain.c [new file with mode: 0644]
src/client/client_terrain.h [new file with mode: 0644]
src/client/cube.c
src/client/cube.h
src/client/debug_menu.c
src/client/debug_menu.h
src/client/facecache.c
src/client/facecache.h
src/client/font.c
src/client/font.h
src/client/frustum.c
src/client/frustum.h
src/client/game.c
src/client/game.h
src/client/gui.c
src/client/gui.h
src/client/input.c
src/client/input.h
src/client/mesh.c
src/client/mesh.h
src/client/model.c [new file with mode: 0644]
src/client/model.h [new file with mode: 0644]
src/client/object.c [deleted file]
src/client/object.h [deleted file]
src/client/scene.c [deleted file]
src/client/scene.h [deleted file]
src/client/shader.c
src/client/shader.h
src/client/sky.c
src/client/sky.h
src/client/terrain_gfx.c [new file with mode: 0644]
src/client/terrain_gfx.h [new file with mode: 0644]
src/client/texture.c
src/client/texture.h
src/client/vertex.c [deleted file]
src/client/vertex.h [deleted file]
src/client/window.c
src/client/window.h
src/color.c [new file with mode: 0644]
src/color.h [new file with mode: 0644]
src/config.c
src/config.h
src/day.c
src/day.h
src/debug.sh
src/debug_loop.sh
src/debug_mapgen.sh [deleted file]
src/debug_terrain.sh [new file with mode: 0755]
src/entity.h [new file with mode: 0644]
src/environment.c
src/environment.h
src/interrupt.c
src/interrupt.h
src/map.c [deleted file]
src/map.h [deleted file]
src/mktypes.sh
src/node.c
src/node.h
src/perlin.h
src/physics.c [new file with mode: 0644]
src/physics.h [new file with mode: 0644]
src/server/biomes.c
src/server/biomes.h
src/server/database.c
src/server/database.h
src/server/mapgen.c [deleted file]
src/server/mapgen.h [deleted file]
src/server/schematic.c [new file with mode: 0644]
src/server/schematic.h [new file with mode: 0644]
src/server/server.c
src/server/server.h
src/server/server_config.c
src/server/server_config.h
src/server/server_map.c [deleted file]
src/server/server_map.h [deleted file]
src/server/server_player.c
src/server/server_player.h
src/server/server_terrain.c [new file with mode: 0644]
src/server/server_terrain.h [new file with mode: 0644]
src/server/terrain_gen.c [new file with mode: 0644]
src/server/terrain_gen.h [new file with mode: 0644]
src/server/trees.c
src/server/trees.h
src/server/voxelctx.c
src/server/voxelctx.h
src/terrain.c [new file with mode: 0644]
src/terrain.h [new file with mode: 0644]
src/types.def
src/util.c [deleted file]
src/util.h [deleted file]
textures/models/player/arm/back.png [new file with mode: 0644]
textures/models/player/arm/bottom.png [new file with mode: 0644]
textures/models/player/arm/front.png [new file with mode: 0644]
textures/models/player/arm/left.png [new file with mode: 0644]
textures/models/player/arm/right.png [new file with mode: 0644]
textures/models/player/arm/top.png [new file with mode: 0644]
textures/models/player/chest/back.png [new file with mode: 0644]
textures/models/player/chest/bottom.png [new file with mode: 0644]
textures/models/player/chest/front.png [new file with mode: 0644]
textures/models/player/chest/left.png [new file with mode: 0644]
textures/models/player/chest/right.png [new file with mode: 0644]
textures/models/player/chest/top.png [new file with mode: 0644]
textures/models/player/head/back.png [new file with mode: 0644]
textures/models/player/head/bottom.png [new file with mode: 0644]
textures/models/player/head/front.png [new file with mode: 0644]
textures/models/player/head/left.png [new file with mode: 0644]
textures/models/player/head/right.png [new file with mode: 0644]
textures/models/player/head/top.png [new file with mode: 0644]
textures/models/player/leg/back.png [new file with mode: 0644]
textures/models/player/leg/bottom.png [new file with mode: 0644]
textures/models/player/leg/front.png [new file with mode: 0644]
textures/models/player/leg/left.png [new file with mode: 0644]
textures/models/player/leg/right.png [new file with mode: 0644]
textures/models/player/leg/top.png [new file with mode: 0644]
textures/oak_wood.png
upload.sh

index ce6db77c320fb8b6757132511178e87588f410cb..4eb449c76539bf19e57e305b3b9746156da1f77a 100644 (file)
@@ -12,10 +12,10 @@ CTestTestfile.cmake
 _deps
 
 # Binaries
-Dragonblocks
-DragonblocksServer
-DragonblocksAlpha-*.zip
-DragonblocksAlpha
+dragonblocks
+dragonblocks_server
+dragonblocks_alpha-*.zip
+dragonblocks_alpha
 
 # Data
 client.conf
index ae8eb62c072d14963bcba10746946384e1b62b3f..6ced1b8ea5d67a45618e27d7d7443808d6ffac06 100644 (file)
 [submodule "deps/endian.h"]
        path = deps/endian.h
        url = https://github.com/dragonblocks/endian.h
-[submodule "deps/dragonport"]
-       path = deps/dragonport
-       url = https://github.com/dragonblocks/dragonport
+[submodule "deps/asprintf"]
+       path = deps/asprintf
+       url = https://github.com/dragonblocks/asprintf
 [submodule "deps/dragonnet"]
        path = deps/dragonnet
        url = https://github.com/dragonblocks/dragonnet
 [submodule "deps/linenoise"]
        path = deps/linenoise
        url = https://github.com/antirez/linenoise
-[submodule "deps/dragontype"]
-       path = deps/dragontype
-       url = https://github.com/dragonblocks/dragontype
+[submodule "deps/protogen"]
+       path = deps/protogen
+       url = https://github.com/dragonblocks/protogen
index 529bb0f82b56e2b6252176f2d6b1981a8e763469..fad75bce23d3bd23f81c068d4de44a2c4f2c8489 100644 (file)
--- a/README.md
+++ b/README.md
@@ -78,6 +78,7 @@ A PC with at least 4 CPU cores is recommended, but not necessarly required.
 - Saving map, player positions and other data to a SQLite3 database
 - Multithreaded map generation, mesh generation and networking
 - Handlers for SIGINT und SIGTERM (just Ctrl+C to shut down the server)
+- Log levels: error, warning, access, action, info, verbose
 
 ## Usage
 
diff --git a/deps/asprintf b/deps/asprintf
new file mode 160000 (submodule)
index 0000000..84c7e7c
--- /dev/null
@@ -0,0 +1 @@
+Subproject commit 84c7e7cb1e0a6ea4102ca785cca31a3e66f34a48
index 164e1ed7f93e1889c36b549a995e93fc0992c888..c346a21deaf3aec0983d0e740d6c0b6799f076ef 160000 (submodule)
@@ -1 +1 @@
-Subproject commit 164e1ed7f93e1889c36b549a995e93fc0992c888
+Subproject commit c346a21deaf3aec0983d0e740d6c0b6799f076ef
diff --git a/deps/dragonport b/deps/dragonport
deleted file mode 160000 (submodule)
index d719d15..0000000
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit d719d15af18415f17ba5ab9cde0718f2e38717ba
index 0f30ec889c7a70ab7f7b79836d1a34ddf8659a47..d439328eed1446e6869a1fd23fe5c0a99350806d 160000 (submodule)
@@ -1 +1 @@
-Subproject commit 0f30ec889c7a70ab7f7b79836d1a34ddf8659a47
+Subproject commit d439328eed1446e6869a1fd23fe5c0a99350806d
diff --git a/deps/dragontype b/deps/dragontype
deleted file mode 160000 (submodule)
index 18ae77e..0000000
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 18ae77e35da46f97f3b06562a4a892ebdeeb9750
diff --git a/deps/protogen b/deps/protogen
new file mode 160000 (submodule)
index 0000000..a7957f4
--- /dev/null
@@ -0,0 +1 @@
+Subproject commit a7957f4abdccb075693b01713492cd67b881d1ec
diff --git a/models/player.txt b/models/player.txt
new file mode 100644 (file)
index 0000000..0fe8485
--- /dev/null
@@ -0,0 +1,16 @@
+name player scale 1 1.8 1
+       name head pos 0 0.875 0 scale 0.45 0.25 0.45 cube head
+       name body scale 0.48 0.75 0.36
+               name upper pos 0 1.0 0 scale 1 0.5 1 
+                       name chest pos 0 -0.5 0 cube chest
+                       name shoulders scale 0.5 1 1
+                               name left pos -1.5 0 0
+                                       name arm pos 0 -0.5 0 cube arm
+                               name right pos +1.5 0 0
+                                       name arm pos 0 -0.5 0 cube arm
+               name lower pos 0 0.5 0 scale 1 0.5 1
+                       name hips scale 0.5 1 1
+                               name left pos -0.5 0 0
+                                       name leg pos 0 -0.5 0 cube leg
+                               name right pos +0.5 0 0
+                                       name leg pos 0 -0.5 0 cube leg
diff --git a/schematics/spawn_hut.gox b/schematics/spawn_hut.gox
new file mode 100644 (file)
index 0000000..7387575
Binary files /dev/null and b/schematics/spawn_hut.gox differ
diff --git a/schematics/spawn_hut.txt b/schematics/spawn_hut.txt
new file mode 100644 (file)
index 0000000..5e33f2d
--- /dev/null
@@ -0,0 +1,429 @@
+# Goxel 0.10.6
+# One line per voxel
+# X Y Z RRGGBB
+0 -3 0 7d5435
+1 -3 0 7d5435
+2 -3 0 7d5435
+3 -3 0 7d5435
+4 -3 0 7d5435
+0 -2 0 7d5435
+1 -2 0 7d5435
+2 -2 0 7d5435
+3 -2 0 7d5435
+4 -2 0 7d5435
+0 -1 0 7d5435
+1 -1 0 7d5435
+2 -1 0 7d5435
+3 -1 0 7d5435
+4 -1 0 7d5435
+0 -3 1 7d5435
+1 -3 1 7d5435
+2 -3 1 7d5435
+3 -3 1 7d5435
+4 -3 1 7d5435
+4 -2 1 7d5435
+4 -1 1 7d5435
+0 -3 2 7d5435
+3 -3 2 7d5435
+4 -3 2 7d5435
+4 -2 2 7d5435
+4 -1 2 7d5435
+0 -3 3 7d5435
+3 -3 3 7d5435
+4 -3 3 7d5435
+4 -2 3 7d5435
+4 -1 3 7d5435
+0 -3 4 7d5435
+1 -3 4 7d5435
+2 -3 4 7d5435
+3 -3 4 7d5435
+4 -3 4 7d5435
+4 -2 4 7d5435
+4 -1 4 7d5435
+0 -4 5 503728
+1 -4 5 503728
+2 -4 5 503728
+3 -4 5 503728
+4 -4 5 503728
+5 -4 5 503728
+0 -3 5 503728
+1 -3 5 503728
+2 -3 5 503728
+3 -3 5 503728
+4 -3 5 503728
+0 -2 5 503728
+1 -2 5 503728
+2 -2 5 503728
+3 -2 5 503728
+4 -2 5 7d5435
+0 -1 5 503728
+1 -1 5 503728
+2 -1 5 503728
+3 -1 5 503728
+4 -1 5 7d5435
+0 -3 6 503728
+1 -3 6 503728
+2 -3 6 503728
+3 -3 6 503728
+4 -3 6 503728
+5 -3 6 503728
+0 -2 6 503728
+1 -2 6 503728
+2 -2 6 503728
+3 -2 6 503728
+4 -2 6 503728
+4 -1 6 7d5435
+0 -2 7 503728
+1 -2 7 503728
+2 -2 7 503728
+3 -2 7 503728
+4 -2 7 503728
+5 -2 7 503728
+0 -1 7 503728
+1 -1 7 503728
+2 -1 7 503728
+3 -1 7 503728
+4 -1 7 503728
+0 -1 8 503728
+1 -1 8 503728
+2 -1 8 503728
+3 -1 8 503728
+4 -1 8 503728
+5 -1 8 503728
+-5 -3 0 7d5435
+-4 -3 0 7d5435
+-3 -3 0 7d5435
+-2 -3 0 7d5435
+-1 -3 0 7d5435
+-5 -2 0 7d5435
+-4 -2 0 7d5435
+-3 -2 0 7d5435
+-2 -2 0 7d5435
+-1 -2 0 7d5435
+-5 -1 0 7d5435
+-4 -1 0 7d5435
+-3 -1 0 7d5435
+-2 -1 0 7d5435
+-1 -1 0 7d5435
+-5 -3 1 7d5435
+-4 -3 1 7d5435
+-3 -3 1 7d5435
+-2 -3 1 7d5435
+-1 -3 1 7d5435
+-5 -2 1 7d5435
+-5 -1 1 7d5435
+-5 -3 2 7d5435
+-4 -3 2 7d5435
+-1 -3 2 7d5435
+-5 -3 3 7d5435
+-4 -3 3 7d5435
+-1 -3 3 7d5435
+-5 -3 4 7d5435
+-4 -3 4 7d5435
+-3 -3 4 7d5435
+-2 -3 4 7d5435
+-1 -3 4 7d5435
+-5 -2 4 7d5435
+-5 -1 4 7d5435
+-6 -4 5 503728
+-5 -4 5 503728
+-4 -4 5 503728
+-3 -4 5 503728
+-2 -4 5 503728
+-1 -4 5 503728
+-5 -3 5 503728
+-4 -3 5 503728
+-3 -3 5 503728
+-2 -3 5 503728
+-1 -3 5 503728
+-5 -2 5 7d5435
+-4 -2 5 503728
+-3 -2 5 503728
+-2 -2 5 503728
+-1 -2 5 503728
+-5 -1 5 7d5435
+-4 -1 5 503728
+-3 -1 5 503728
+-2 -1 5 503728
+-1 -1 5 503728
+-6 -3 6 503728
+-5 -3 6 503728
+-4 -3 6 503728
+-3 -3 6 503728
+-2 -3 6 503728
+-1 -3 6 503728
+-5 -2 6 503728
+-4 -2 6 503728
+-3 -2 6 503728
+-2 -2 6 503728
+-1 -2 6 503728
+-5 -1 6 7d5435
+-6 -2 7 503728
+-5 -2 7 503728
+-4 -2 7 503728
+-3 -2 7 503728
+-2 -2 7 503728
+-1 -2 7 503728
+-5 -1 7 503728
+-4 -1 7 503728
+-3 -1 7 503728
+-2 -1 7 503728
+-1 -1 7 503728
+-6 -1 8 503728
+-5 -1 8 503728
+-4 -1 8 503728
+-3 -1 8 503728
+-2 -1 8 503728
+-1 -1 8 503728
+0 0 0 7d5435
+1 0 0 7d5435
+2 0 0 7d5435
+3 0 0 7d5435
+4 0 0 7d5435
+0 1 0 7d5435
+1 1 0 7d5435
+2 1 0 7d5435
+3 1 0 7d5435
+4 1 0 7d5435
+0 2 0 7d5435
+1 2 0 7d5435
+2 2 0 7d5435
+3 2 0 7d5435
+4 2 0 7d5435
+0 3 0 7d5435
+1 3 0 7d5435
+2 3 0 7d5435
+3 3 0 7d5435
+4 3 0 7d5435
+0 4 0 7d5435
+1 4 0 7d5435
+2 4 0 7d5435
+3 4 0 7d5435
+4 4 0 7d5435
+4 2 1 7d5435
+4 3 1 7d5435
+0 4 1 7d5435
+1 4 1 7d5435
+2 4 1 7d5435
+3 4 1 7d5435
+4 4 1 7d5435
+4 2 2 7d5435
+4 3 2 7d5435
+1 4 2 7d5435
+4 4 2 7d5435
+4 2 3 7d5435
+4 3 3 7d5435
+1 4 3 7d5435
+4 4 3 7d5435
+4 0 4 7d5435
+4 1 4 7d5435
+4 2 4 7d5435
+4 3 4 7d5435
+0 4 4 7d5435
+1 4 4 7d5435
+2 4 4 7d5435
+3 4 4 7d5435
+4 4 4 7d5435
+0 0 5 503728
+1 0 5 503728
+2 0 5 503728
+3 0 5 503728
+4 0 5 7d5435
+0 1 5 503728
+1 1 5 503728
+2 1 5 503728
+3 1 5 503728
+4 1 5 7d5435
+0 2 5 503728
+1 2 5 503728
+2 2 5 503728
+3 2 5 503728
+4 2 5 7d5435
+0 3 5 503728
+1 3 5 503728
+2 3 5 503728
+3 3 5 503728
+4 3 5 7d5435
+0 4 5 503728
+1 4 5 503728
+2 4 5 503728
+3 4 5 503728
+4 4 5 503728
+0 5 5 503728
+1 5 5 503728
+2 5 5 503728
+3 5 5 503728
+4 5 5 503728
+5 5 5 503728
+4 0 6 7d5435
+4 1 6 7d5435
+4 2 6 7d5435
+0 3 6 503728
+1 3 6 503728
+2 3 6 503728
+3 3 6 503728
+4 3 6 503728
+0 4 6 503728
+1 4 6 503728
+2 4 6 503728
+3 4 6 503728
+4 4 6 503728
+5 4 6 503728
+4 0 7 503728
+4 1 7 503728
+0 2 7 503728
+1 2 7 503728
+2 2 7 503728
+3 2 7 503728
+4 2 7 503728
+0 3 7 503728
+1 3 7 503728
+2 3 7 503728
+3 3 7 503728
+4 3 7 503728
+5 3 7 503728
+0 0 8 503728
+1 0 8 503728
+2 0 8 503728
+3 0 8 503728
+4 0 8 503728
+5 0 8 503728
+0 1 8 503728
+1 1 8 503728
+2 1 8 503728
+3 1 8 503728
+4 1 8 503728
+5 1 8 503728
+0 2 8 503728
+1 2 8 503728
+2 2 8 503728
+3 2 8 503728
+4 2 8 503728
+5 2 8 503728
+-5 0 0 7d5435
+-4 0 0 7d5435
+-3 0 0 7d5435
+-2 0 0 7d5435
+-1 0 0 7d5435
+-5 1 0 7d5435
+-4 1 0 7d5435
+-3 1 0 7d5435
+-2 1 0 7d5435
+-1 1 0 7d5435
+-5 2 0 7d5435
+-4 2 0 7d5435
+-3 2 0 7d5435
+-2 2 0 7d5435
+-1 2 0 7d5435
+-5 3 0 7d5435
+-4 3 0 7d5435
+-3 3 0 7d5435
+-2 3 0 7d5435
+-1 3 0 7d5435
+-5 4 0 7d5435
+-4 4 0 7d5435
+-3 4 0 7d5435
+-2 4 0 7d5435
+-1 4 0 7d5435
+-5 0 1 7d5435
+-5 1 1 7d5435
+-5 2 1 7d5435
+-5 3 1 7d5435
+-5 4 1 7d5435
+-4 4 1 7d5435
+-3 4 1 7d5435
+-2 4 1 7d5435
+-1 4 1 7d5435
+-5 1 2 7d5435
+-5 2 2 7d5435
+-5 4 2 7d5435
+-3 4 2 7d5435
+-5 1 3 7d5435
+-5 2 3 7d5435
+-5 4 3 7d5435
+-3 4 3 7d5435
+-5 0 4 7d5435
+-5 1 4 7d5435
+-5 2 4 7d5435
+-5 3 4 7d5435
+-5 4 4 7d5435
+-4 4 4 7d5435
+-3 4 4 7d5435
+-2 4 4 7d5435
+-1 4 4 7d5435
+-5 0 5 7d5435
+-4 0 5 503728
+-3 0 5 503728
+-2 0 5 503728
+-1 0 5 503728
+-5 1 5 7d5435
+-4 1 5 503728
+-3 1 5 503728
+-2 1 5 503728
+-1 1 5 503728
+-5 2 5 7d5435
+-4 2 5 503728
+-3 2 5 503728
+-2 2 5 503728
+-1 2 5 503728
+-5 3 5 7d5435
+-4 3 5 503728
+-3 3 5 503728
+-2 3 5 503728
+-1 3 5 503728
+-5 4 5 503728
+-4 4 5 503728
+-3 4 5 503728
+-2 4 5 503728
+-1 4 5 503728
+-6 5 5 503728
+-5 5 5 503728
+-4 5 5 503728
+-3 5 5 503728
+-2 5 5 503728
+-1 5 5 503728
+-5 0 6 7d5435
+-5 1 6 7d5435
+-5 2 6 7d5435
+-5 3 6 503728
+-4 3 6 503728
+-3 3 6 503728
+-2 3 6 503728
+-1 3 6 503728
+-6 4 6 503728
+-5 4 6 503728
+-4 4 6 503728
+-3 4 6 503728
+-2 4 6 503728
+-1 4 6 503728
+-5 0 7 503728
+-5 1 7 503728
+-5 2 7 503728
+-4 2 7 503728
+-3 2 7 503728
+-2 2 7 503728
+-1 2 7 503728
+-6 3 7 503728
+-5 3 7 503728
+-4 3 7 503728
+-3 3 7 503728
+-2 3 7 503728
+-1 3 7 503728
+-6 0 8 503728
+-5 0 8 503728
+-4 0 8 503728
+-3 0 8 503728
+-2 0 8 503728
+-1 0 8 503728
+-6 1 8 503728
+-5 1 8 503728
+-4 1 8 503728
+-3 1 8 503728
+-2 1 8 503728
+-1 1 8 503728
+-6 2 8 503728
+-5 2 8 503728
+-4 2 8 503728
+-3 2 8 503728
+-2 2 8 503728
+-1 2 8 503728
diff --git a/shaders/3d/fragment.glsl b/shaders/3d/fragment.glsl
deleted file mode 100755 (executable)
index 58b06bd..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-in vec3 fragmentPosition;
-in vec3 fragmentNormal;
-in float fragmentTextureIndex;
-in vec2 fragmentTextureCoords;
-in vec3 fragmentColor;
-
-out vec4 outColor;
-
-uniform vec3 fogColor;
-uniform vec3 cameraPos;
-uniform sampler2D textures[MAX_TEXTURE_UNITS];
-
-void main()
-{
-       outColor = texture(textures[int(fragmentTextureIndex + 0.5)], fragmentTextureCoords) * vec4(fragmentColor, 1.0);
-       outColor.rgb = mix(outColor.rgb, fogColor, clamp(length(fragmentPosition - cameraPos) / RENDER_DISTANCE, 0.0, 1.0));
-
-       if (outColor.a == 0.0)
-               discard;
-}
diff --git a/shaders/3d/vertex.glsl b/shaders/3d/vertex.glsl
deleted file mode 100755 (executable)
index 8d48d53..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-layout(location = 0) in vec3 vertexPosition;
-layout(location = 1) in vec3 vertexNormal;
-layout(location = 2) in float vertexTextureIndex;
-layout(location = 3) in vec2 vertexTextureCoords;
-layout(location = 4) in vec3 vertexColor;
-
-out vec3 fragmentPosition;
-out vec3 fragmentNormal;
-out float fragmentTextureIndex;
-out vec2 fragmentTextureCoords;
-out vec3 fragmentColor;
-
-uniform mat4 model;
-uniform mat4 VP;
-uniform float daylight;
-uniform float ambientLight;
-uniform vec3 lightDir;
-
-void main()
-{
-       vec4 worldSpace = model * vec4(vertexPosition, 1.0);
-       gl_Position = VP * worldSpace;
-
-       fragmentPosition = worldSpace.xyz;
-       fragmentNormal = vertexNormal;
-       fragmentTextureIndex = vertexTextureIndex;
-       fragmentTextureCoords = vertexTextureCoords;
-       fragmentColor = vertexColor;
-
-       float diffuseLight = 0.3 * daylight * clamp(dot(normalize(fragmentNormal), normalize(lightDir)), 0.0, 1.0);
-       float light = ambientLight + diffuseLight;
-
-       fragmentColor *= light;
-}
diff --git a/shaders/terrain/fragment.glsl b/shaders/terrain/fragment.glsl
new file mode 100755 (executable)
index 0000000..533c91c
--- /dev/null
@@ -0,0 +1,20 @@
+in vec3 fragmentPosition;
+in vec3 fragmentNormal;
+in vec2 fragmentTextureCoords;
+in float fragmentTextureIndex;
+in vec3 fragmentColor;
+
+out vec4 outColor;
+
+uniform vec3 fogColor;
+uniform vec3 cameraPos;
+uniform sampler2D textures[MAX_TEXTURE_UNITS];
+
+void main()
+{
+       outColor = texture(textures[int(fragmentTextureIndex + 0.5)], fragmentTextureCoords) * vec4(fragmentColor, 1.0);
+       outColor.rgb = mix(outColor.rgb, fogColor, clamp(length(fragmentPosition - cameraPos) / VIEW_DISTANCE, 0.0, 1.0));
+
+       if (outColor.a == 0.0)
+               discard;
+}
diff --git a/shaders/terrain/vertex.glsl b/shaders/terrain/vertex.glsl
new file mode 100755 (executable)
index 0000000..7104497
--- /dev/null
@@ -0,0 +1,34 @@
+layout(location = 0) in vec3 vertexPosition;
+layout(location = 1) in vec3 vertexNormal;
+layout(location = 2) in vec2 vertexTextureCoords;
+layout(location = 3) in float vertexTextureIndex;
+layout(location = 4) in vec3 vertexColor;
+
+out vec3 fragmentPosition;
+out vec3 fragmentNormal;
+out vec2 fragmentTextureCoords;
+out float fragmentTextureIndex;
+out vec3 fragmentColor;
+
+uniform mat4 model;
+uniform mat4 VP;
+uniform float daylight;
+uniform float ambientLight;
+uniform vec3 lightDir;
+
+void main()
+{
+       vec4 worldSpace = model * vec4(vertexPosition, 1.0);
+       gl_Position = VP * worldSpace;
+
+       fragmentPosition = worldSpace.xyz;
+       fragmentNormal = vertexNormal;
+       fragmentTextureCoords = vertexTextureCoords;
+       fragmentTextureIndex = vertexTextureIndex;
+       fragmentColor = vertexColor;
+
+       float diffuseLight = 0.3 * daylight * clamp(dot(normalize(fragmentNormal), normalize(lightDir)), 0.0, 1.0);
+       float light = ambientLight + diffuseLight;
+
+       fragmentColor *= light;
+}
index 120f4a29d5ba596de71edd50cb30a6031424eacb..b1961d067b7e044989286ace0227ac87af117208 100755 (executable)
@@ -1,2 +1,4 @@
-#! /bin/bash
-./DragonblocksServer "[::1]:4000" & echo "singleplayer" | ./Dragonblocks "[::1]:4000"; killall DragonblocksServer
+#!/bin/bash
+./dragonblocks_server "[::1]:4000" &
+echo "singleplayer" | ./dragonblocks "[::1]:4000"
+pkill -P $$
index 6d4363d42485154a00bf6273194a174b7c52cac8..66b6882c656541b0f059a5ff372db2edb19806e8 100755 (executable)
@@ -1,4 +1,9 @@
-#! /bin/bash
+#!/bin/bash
+VERSION=`git tag --points-at HEAD`
+if [[ $VERSION = "" ]]; then
+       VERSION=`git rev-parse --short HEAD`
+fi
+DIR=dragonblocks_alpha-$VERSION
 mkdir .build
 cp -r * .build/
 cd .build/
@@ -9,14 +14,10 @@ if ! (cmake -B . -S ../src -DCMAKE_BUILD_TYPE=Release -DRESSOURCE_PATH="\"\"" &&
        rm -rf .build
        exit 1
 fi
-cp Dragonblocks DragonblocksServer ..
+cp dragonblocks dragonblocks_server ..
 cd ..
-rm -rf .git* deps src build BUILDING.md snapshot.sh upload.sh DragonblocksAlpha-*.zip DragonblocksAlpha screenshot-*.png
+rm -rf .git* deps src build BUILDING.md snapshot.sh upload.sh dragonblocks_alpha-* screenshot-*.png
 cd ..
-mv .build DragonblocksAlpha
-VERSION=`git tag --points-at HEAD`
-if [[ $VERSION = "" ]]; then
-       VERSION=`git rev-parse --short HEAD`
-fi
-zip -r DragonblocksAlpha-$VERSION.zip DragonblocksAlpha/*
-rm -rf DragonblocksAlpha
+mv .build $DIR
+zip -r $DIR.zip $DIR/*
+rm -rf $DIR
index 879bec10c9446e9a32a3f6a13a12ecccdaac4245..0a9c49684f374918aae85f0679d1a8bfb145400f 100644 (file)
@@ -22,10 +22,10 @@ find_package(Freetype REQUIRED)
 
 # Options
 
-add_compile_definitions("RESSOURCE_PATH=\"${RESSOURCE_PATH}\"")
 add_compile_definitions("USE_DRAGONNET")
+add_compile_definitions("RESSOURCE_PATH=\"${RESSOURCE_PATH}\"")
 
-add_compile_options(-Wall -Wextra -Werror -fmax-errors=4)
+add_compile_options(-Wall -Wextra -Werror -Wno-address-of-packed-member -fmax-errors=4)
 
 link_libraries(
        pthread
@@ -51,43 +51,47 @@ endif()
 # Common sources
 
 set(COMMON_SOURCES
+       "${DEPS_DIR}/asprintf/asprintf.c"
        "${DEPS_DIR}/dragonnet/addr.c"
        "${DEPS_DIR}/dragonnet/listen.c"
        "${DEPS_DIR}/dragonnet/peer.c"
        "${DEPS_DIR}/dragonnet/recv.c"
        "${DEPS_DIR}/dragonnet/recv_thread.c"
        "${DEPS_DIR}/dragonnet/send.c"
-       "${DEPS_DIR}/dragonport/asprintf.c"
        "${DEPS_DIR}/dragonstd/array.c"
-       "${DEPS_DIR}/dragonstd/bintree.c"
        "${DEPS_DIR}/dragonstd/flag.c"
        "${DEPS_DIR}/dragonstd/list.c"
+       "${DEPS_DIR}/dragonstd/map.c"
        "${DEPS_DIR}/dragonstd/queue.c"
+       "${DEPS_DIR}/dragonstd/refcount.c"
+       "${DEPS_DIR}/dragonstd/tree.c"
+       "${DEPS_DIR}/dragonstd/bits/compare.c"
        "${DEPS_DIR}/linenoise/linenoise.c"
        "${DEPS_DIR}/perlin/perlin.c"
+       color.c
        config.c
        day.c
        environment.c
        interrupt.c
-       map.c
        node.c
        perlin.c
+       physics.c
+       terrain.c
        types.c
-       util.c
 )
 
 # Client
 
-add_executable(Dragonblocks
+add_executable(dragonblocks
        ${COMMON_SOURCES}
-       client/blockmesh.c
        client/camera.c
        client/client.c
        client/client_auth.c
        client/client_config.c
-       client/client_map.c
+       client/client_entity.c
        client/client_node.c
        client/client_player.c
+       client/client_terrain.c
        client/cube.c
        client/debug_menu.c
        client/facecache.c
@@ -97,53 +101,53 @@ add_executable(Dragonblocks
        client/gui.c
        client/input.c
        client/mesh.c
-       client/object.c
-       client/scene.c
+       client/model.c
        client/shader.c
        client/sky.c
+       client/terrain_gfx.c
        client/texture.c
-       client/vertex.c
        client/window.c
 )
 
-target_link_libraries(Dragonblocks
+target_link_libraries(dragonblocks
        ${OPENGL_LIBRARIES}
        ${GLEW_LIBRARIES}
        glfw
        ${FREETYPE_LIBRARIES}
 )
 
-target_include_directories(Dragonblocks PUBLIC
+target_include_directories(dragonblocks PUBLIC
        ${FREETYPE_INCLUDE_DIRS}
 )
 
 # Server
 
-add_executable(DragonblocksServer
+add_executable(dragonblocks_server
        ${COMMON_SOURCES}
        server/biomes.c
        server/database.c
-       server/mapgen.c
+       server/schematic.c
        server/server.c
        server/server_config.c
-       server/server_map.c
        server/server_player.c
+       server/server_terrain.c
+       server/terrain_gen.c
        server/trees.c
        server/voxelctx.c
 )
 
-target_link_libraries(DragonblocksServer
+target_link_libraries(dragonblocks_server
        sqlite3
 )
 
 # Version
 
-add_custom_target(Version
+add_custom_target(version
        COMMAND ${CMAKE_COMMAND} -DSOURCE_DIR=${CMAKE_SOURCE_DIR} -P ${CMAKE_SOURCE_DIR}/version.cmake
 )
 
-add_dependencies(Dragonblocks Version)
-add_dependencies(DragonblocksServer Version)
+add_dependencies(dragonblocks version)
+add_dependencies(dragonblocks_server version)
 
 # Types
 
@@ -154,9 +158,9 @@ add_custom_command(
        WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
 )
 
-add_custom_target(DragonnetTypes
+add_custom_target(types
        DEPENDS "${CMAKE_SOURCE_DIR}/types.c" "${CMAKE_SOURCE_DIR}/types.h"
 )
 
-add_dependencies(Dragonblocks DragonnetTypes)
-add_dependencies(DragonblocksServer DragonnetTypes)
+add_dependencies(dragonblocks types)
+add_dependencies(dragonblocks_server types)
diff --git a/src/client/blockmesh.c b/src/client/blockmesh.c
deleted file mode 100644 (file)
index 25274e2..0000000
+++ /dev/null
@@ -1,130 +0,0 @@
-#include "client/blockmesh.h"
-#include "client/client_map.h"
-#include "client/client_node.h"
-#include "client/cube.h"
-
-static v3s8 fdir[6] = {
-       {+0, +0, -1},
-       {+0, +0, +1},
-       {-1, +0, +0},
-       {+1, +0, +0},
-       {+0, -1, +0},
-       {+0, +1, +0},
-};
-
-static s32 half_block_size = MAPBLOCK_SIZE / 2;
-
-static void make_vertices(Object *object, MapBlock *block, bool hide_edges)
-{
-       object->visible = false;
-       v3s32 node_bp = {
-               block->pos.x * MAPBLOCK_SIZE,
-               block->pos.y * MAPBLOCK_SIZE,
-               block->pos.z * MAPBLOCK_SIZE
-       };
-
-       ITERATE_MAPBLOCK {
-               MapNode *node = &block->data[x][y][z];
-               ClientNodeDefinition *def = &client_node_definitions[node->type];
-
-               if (def->visibility != NV_NONE) {
-                       v3f32 offset = {
-                               x - half_block_size - 0.5,
-                               y - half_block_size - 0.5,
-                               z - half_block_size - 0.5
-                       };
-
-                       for (int f = 0; f < 6; f++) {
-                               v3s8 npos = {
-                                       x + fdir[f].x,
-                                       y + fdir[f].y,
-                                       z + fdir[f].z,
-                               };
-
-                               bool direct_neighbor = npos.x >= 0 && npos.x < MAPBLOCK_SIZE
-                                       && npos.y >= 0 && npos.y < MAPBLOCK_SIZE
-                                       && npos.z >= 0 && npos.z < MAPBLOCK_SIZE;
-
-                               MapNode neighbor = direct_neighbor
-                                       ? block->data[npos.x][npos.y][npos.z]
-                                       : map_get_node(client_map.map, (v3s32) {npos.x + node_bp.x, npos.y + node_bp.y, npos.z + node_bp.z});
-
-                               bool transparency_edge = def->visibility != NV_BLEND || neighbor.type != node->type;
-
-                               bool render_node = def->visibility == NV_CLIP || (neighbor.type != NODE_UNLOADED
-                                       && client_node_definitions[neighbor.type].visibility != NV_SOLID
-                                       && transparency_edge);
-
-                               object->visible = object->visible || render_node;
-
-                               if (! hide_edges && ! direct_neighbor)
-                                       render_node = transparency_edge;
-
-                               if (render_node) {
-                                       object->transparent = object->transparent || def->visibility == NV_BLEND;
-                                       object_set_texture(object, def->tiles.textures[f]);
-
-                                       for (int v = 0; v < 6; v++) {
-                                               Vertex3D vertex = cube_vertices[f][v];
-                                               vertex.position.x += offset.x;
-                                               vertex.position.y += offset.y;
-                                               vertex.position.z += offset.z;
-
-                                               if (def->render)
-                                                       def->render((v3s32) {x + node_bp.x, y + node_bp.y, z + node_bp.z}, node, &vertex, f, v);
-
-                                               object_add_vertex(object, &vertex);
-                                       }
-                               }
-                       }
-               }
-       }
-}
-
-static void animate_mapblock_mesh(Object *obj, f64 dtime)
-{
-       obj->scale.x += dtime * 2.0;
-
-       if (obj->scale.x > 1.0f) {
-               obj->scale.x = 1.0f;
-               client_map_schedule_update_block_mesh(obj->extra);
-       }
-
-       obj->scale.z = obj->scale.y = obj->scale.x;
-
-       object_transform(obj);
-}
-
-void blockmesh_make(MapBlock *block)
-{
-       MapBlockExtraData *extra = block->extra;
-
-       Object *obj = object_create();
-
-       obj->pos = (v3f32) {block->pos.x * MAPBLOCK_SIZE + half_block_size + 0.5f, block->pos.y * MAPBLOCK_SIZE + half_block_size + 0.5f, block->pos.z * MAPBLOCK_SIZE + half_block_size + 0.5f};
-       obj->scale = (v3f32) {0.1f, 0.1f, 0.1f};
-       obj->frustum_culling = true;
-       obj->box = (aabb3f32) {{-half_block_size - 1.0f, -half_block_size - 1.0f, -half_block_size - 1.0f}, {half_block_size + 1.0f, half_block_size + 1.0f, half_block_size + 1.0f}};
-       obj->on_render = (obj->scale.x == 1.0f) ? NULL : &animate_mapblock_mesh;
-       obj->extra = block;
-
-       make_vertices(obj, block, obj->scale.x == 1.0f);
-
-       if (! object_add_to_scene(obj)) {
-               object_delete(obj);
-               obj = NULL;
-       }
-
-       pthread_mutex_lock(&block->mtx);
-       if (extra->obj) {
-               if (obj) {
-                       obj->scale = extra->obj->scale;
-                       object_transform(obj);
-               }
-
-               extra->obj->remove = true;
-       }
-
-       extra->obj = obj;
-       pthread_mutex_unlock(&block->mtx);
-}
diff --git a/src/client/blockmesh.h b/src/client/blockmesh.h
deleted file mode 100644 (file)
index f469a06..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-#ifndef _BLOCKMESH_H_
-#define _BLOCKMESH_H_
-
-#include "map.h"
-
-void blockmesh_make(MapBlock *block);
-
-#endif
index 4cea5a3d22d417d1ade31f22c0a90b535a120957..adb23647884cd1784d5b1a15d4d1dbff7c2c0e9f 100644 (file)
@@ -1,6 +1,6 @@
 #include <math.h>
-#include "client/client.h"
 #include "client/camera.h"
+#include "client/client.h"
 
 struct Camera camera;
 
index bc7d57c826b58246b18ba36ec54fcef46ba920ad..abe7d8f0a83fd3cc8b242f60a02896a39e3759f4 100644 (file)
@@ -6,8 +6,7 @@
 #include <linmath.h/linmath.h>
 #include "types.h"
 
-extern struct Camera
-{
+extern struct Camera {
        mat4x4 view;
        vec3 eye, front, right, up;
        struct {
@@ -21,4 +20,4 @@ void camera_set_position(v3f32 pos);
 void camera_set_angle(f32 yaw, f32 pitch);
 void camera_on_resize(int width, int height);
 
-#endif
+#endif // _CAMERA_H_
index 63d3c5946e2c2cae69a64234a79e3a5c3eda0814..0dd8626a28623d056c6eda73c3ff78d459c756eb 100644 (file)
+#define _GNU_SOURCE // don't worry, GNU extensions are only used when available
+#include <dragonstd/flag.h>
 #include <stdio.h>
-#include <string.h>
 #include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
 #include <unistd.h>
-#include <dragonstd/flag.h>
 #include "client/client.h"
 #include "client/client_auth.h"
-#include "client/client_map.h"
 #include "client/client_player.h"
+#include "client/client_terrain.h"
+#include "client/debug_menu.h"
 #include "client/game.h"
 #include "client/input.h"
 #include "day.h"
 #include "interrupt.h"
 #include "perlin.h"
 #include "types.h"
-#include "util.h"
 
 DragonnetPeer *client;
-static Flag *finish;
 
-static bool on_recv(unused DragonnetPeer *peer, DragonnetTypeId type, unused void *pkt)
+static Flag finish;
+static Flag gfx_init;
+
+static bool on_recv(__attribute__((unused)) DragonnetPeer *peer, DragonnetTypeId type, __attribute__((unused)) void *pkt)
 {
-       return (client_auth.state == AUTH_WAIT) == (type == DRAGONNET_TYPE_ToClientAuth);
+       bool allowed = false;
+       pthread_mutex_lock(&client_auth.mtx);
+
+       // this code exists to stop malicious or malfunctioning packets
+       switch (client_auth.state) {
+               // the server shouldn't send anything during auth preparation, drop it
+               case AUTH_INIT:
+                       allowed = false;
+                       break;
+
+               // only the auth packet is allowed before auth is finished
+               case AUTH_WAIT:
+                       allowed = type == DRAGONNET_TYPE_ToClientAuth;
+                       break;
+
+               // don't process auth packets when auth is already finished
+               case AUTH_SUCCESS:
+                       allowed = type != DRAGONNET_TYPE_ToClientAuth;
+                       break;
+       }
+
+       /*
+               It is important that the auth state does not change to until the packet is
+                       processed.
+
+               However, the only state change done by other threads is AUTH_INIT -> AUTH_WAIT,
+                       which is not problematic since packets that are received during AUTH_INIT
+                       are not processed, they are always dropped.
+
+               Therefore the mutex can be unlocked at this point.
+       */
+       pthread_mutex_unlock(&client_auth.mtx);
+       return allowed;
 }
 
-static void on_disconnect(unused DragonnetPeer *peer)
+static void on_disconnect(__attribute__((unused)) DragonnetPeer *peer)
 {
-       flag_set(interrupt);
-       flag_wait(finish);
+       flag_set(&interrupt);
+       // don't free the connection before all other client components have shut down
+       flag_slp(&finish);
 }
 
-static void on_ToClientAuth(unused DragonnetPeer *peer, ToClientAuth *pkt)
+static void on_ToClientAuth(__attribute__((unused)) DragonnetPeer *peer, ToClientAuth *pkt)
 {
+       pthread_mutex_lock(&client_auth.mtx);
        if (pkt->success) {
                client_auth.state = AUTH_SUCCESS;
-               printf("Authenticated successfully\n");
+               printf("[access] authenticated successfully\n");
        } else {
                client_auth.state = AUTH_INIT;
-               printf("Authentication failed, please try again\n");
+               printf("[access] authentication failed, please try again\n");
        }
+       pthread_cond_signal(&client_auth.cv);
+       pthread_mutex_unlock(&client_auth.mtx);
+
+       // yield the connection until the game is fully initialized
+       if (pkt->success)
+               flag_slp(&gfx_init);
 }
 
-static void on_ToClientBlock(unused DragonnetPeer *peer, ToClientBlock *pkt)
+static void on_ToClientChunk(__attribute__((unused)) DragonnetPeer *peer, ToClientChunk *pkt)
 {
-       MapBlock *block = map_get_block(client_map.map, pkt->pos, true);
+       TerrainChunk *chunk = terrain_get_chunk(client_terrain, pkt->pos, true);
 
-       map_deserialize_block(block, pkt->data);
-       ((MapBlockExtraData *) block->extra)->all_air = (pkt->data.siz == 0);
-       client_map_block_received(block);
+       terrain_deserialize_chunk(chunk, pkt->data);
+       ((TerrainChunkMeta *) chunk->extra)->empty = (pkt->data.siz == 0);
+       client_terrain_chunk_received(chunk);
 }
 
-static void on_ToClientInfo(unused DragonnetPeer *peer, ToClientInfo *pkt)
+static void on_ToClientInfo(__attribute__((unused)) DragonnetPeer *peer, ToClientInfo *pkt)
 {
-       client_map_set_simulation_distance(pkt->simulation_distance);
+       client_terrain_set_load_distance(pkt->load_distance);
        seed = pkt->seed;
 }
 
-static void on_ToClientPos(unused DragonnetPeer *peer, ToClientPos *pkt)
+static void on_ToClientTimeOfDay(__attribute__((unused)) DragonnetPeer *peer, ToClientTimeOfDay *pkt)
 {
-       client_player_set_position(pkt->pos);
+       set_time_of_day(pkt->time_of_day);
 }
 
-static void on_ToClientTimeOfDay(unused DragonnetPeer *peer, ToClientTimeOfDay *pkt)
+static void on_ToClientMovement(__attribute__((unused)) DragonnetPeer *peer, ToClientMovement *pkt)
 {
-       set_time_of_day(pkt->time_of_day);
+       pthread_rwlock_wrlock(&client_player.lock_movement);
+       client_player.movement = *pkt;
+       pthread_rwlock_unlock(&client_player.lock_movement);
+
+       debug_menu_changed(ENTRY_FLIGHT);
+       debug_menu_changed(ENTRY_COLLISION);
 }
 
 int main(int argc, char **argv)
 {
+#ifdef __GLIBC__ // check whether bloat is enabled
+       pthread_setname_np(pthread_self(), "main");
+#endif // __GLIBC__
+
        if (argc < 2) {
-               fprintf(stderr, "Missing address\n");
+               fprintf(stderr, "[error] missing address\n");
                return EXIT_FAILURE;
        }
 
-       if (! (client = dragonnet_connect(argv[1]))) {
-               fprintf(stderr, "Failed to connect to server\n");
+       if (!(client = dragonnet_connect(argv[1]))) {
+               fprintf(stderr, "[error] failed to connect to server\n");
                return EXIT_FAILURE;
        }
 
-       client->on_disconnect = &on_disconnect;
-       client->on_recv = &on_recv;
-       client->on_recv_type[DRAGONNET_TYPE_ToClientAuth     ] = (void *) &on_ToClientAuth;
-       client->on_recv_type[DRAGONNET_TYPE_ToClientBlock    ] = (void *) &on_ToClientBlock;
-       client->on_recv_type[DRAGONNET_TYPE_ToClientInfo     ] = (void *) &on_ToClientInfo;
-       client->on_recv_type[DRAGONNET_TYPE_ToClientPos      ] = (void *) &on_ToClientPos;
-       client->on_recv_type[DRAGONNET_TYPE_ToClientTimeOfDay] = (void *) &on_ToClientTimeOfDay;
+       char *address = dragonnet_addr_str(client->raddr);
+       printf("[access] connected to %s\n", address);
+       free(address);
 
-       finish = flag_create();
+       client->on_disconnect = &on_disconnect;
+       client->on_recv                                                  = (void *) &on_recv;
+       client->on_recv_type[DRAGONNET_TYPE_ToClientAuth               ] = (void *) &on_ToClientAuth;
+       client->on_recv_type[DRAGONNET_TYPE_ToClientChunk              ] = (void *) &on_ToClientChunk;
+       client->on_recv_type[DRAGONNET_TYPE_ToClientInfo               ] = (void *) &on_ToClientInfo;
+       client->on_recv_type[DRAGONNET_TYPE_ToClientTimeOfDay          ] = (void *) &on_ToClientTimeOfDay;
+       client->on_recv_type[DRAGONNET_TYPE_ToClientMovement           ] = (void *) &on_ToClientMovement;
+       client->on_recv_type[DRAGONNET_TYPE_ToClientEntityAdd          ] = (void *) &client_entity_add;
+       client->on_recv_type[DRAGONNET_TYPE_ToClientEntityRemove       ] = (void *) &client_entity_remove;
+       client->on_recv_type[DRAGONNET_TYPE_ToClientEntityUpdatePosRot ] = (void *) &client_entity_update_pos_rot;
+       client->on_recv_type[DRAGONNET_TYPE_ToClientEntityUpdateBoxEye ] = (void *) &client_entity_update_box_eye;
+       client->on_recv_type[DRAGONNET_TYPE_ToClientEntityUpdateNametag] = (void *) &client_entity_update_nametag;
+
+       flag_ini(&finish);
+       flag_ini(&gfx_init);
 
        interrupt_init();
-       client_map_init();
+       client_terrain_init();
        client_player_init();
+       client_entity_init();
        dragonnet_peer_run(client);
 
-       if (! client_auth_init())
+       if (!client_auth_init())
                return EXIT_FAILURE;
 
-       if (! game())
+       if (!game(&gfx_init))
                return EXIT_FAILURE;
 
        dragonnet_peer_shutdown(client);
        client_auth_deinit();
+       client_entity_deinit();
        client_player_deinit();
-       client_map_deinit();
+       client_terrain_deinit();
        interrupt_deinit();
 
        pthread_t recv_thread = client->recv_thread;
 
-       flag_set(finish);
+       flag_set(&finish);
        pthread_join(recv_thread, NULL);
 
-       flag_delete(finish);
+       flag_dst(&finish);
+       flag_dst(&gfx_init);
 
        return EXIT_SUCCESS;
 }
index 51ad358e63e7ecd3dbc6438fd160539c0e2ccecb..28b89df6e61b108a5b61bac469429c2e3f4f8fc7 100644 (file)
@@ -5,4 +5,4 @@
 
 extern DragonnetPeer *client;
 
-#endif
+#endif // _CLIENT_H_
index d2db7e042b4ac04b4470d30300b43482e5b7d746..4782d32a5f7f7043e17c963a62399b3ccba0aaf9 100644 (file)
@@ -6,48 +6,58 @@
 #include "interrupt.h"
 #include "types.h"
 
-volatile struct ClientAuth client_auth;
+struct ClientAuth client_auth;
 
-static bool name_prompt()
+static void auth_loop()
 {
-       if (! (client_auth.name = linenoise("Enter name: ")))
-               return false;
+       while (!interrupt.set) switch (client_auth.state) {
+               case AUTH_INIT:
+                       if (client_auth.name)
+                               linenoiseFree(client_auth.name);
 
-       printf("Authenticating as %s...\n", client_auth.name);
-       client_auth.state = AUTH_WAIT;
+                       if (!(client_auth.name = linenoise("Enter name: ")))
+                               return;
 
-       dragonnet_peer_send_ToServerAuth(client, &(ToServerAuth) {
-               .name = client_auth.name,
-       });
+                       printf("[access] authenticating as %s...\n", client_auth.name);
+                       client_auth.state = AUTH_WAIT;
 
-       return true;
+                       dragonnet_peer_send_ToServerAuth(client, &(ToServerAuth) {
+                               .name = client_auth.name,
+                       });
+
+                       __attribute__((fallthrough));
+
+               case AUTH_WAIT:
+                       pthread_cond_wait(&client_auth.cv, &client_auth.mtx);
+                       break;
+
+               case AUTH_SUCCESS:
+                       return;
+       }
 }
 
 bool client_auth_init()
 {
+       client_auth.name = NULL;
+       pthread_cond_init(&client_auth.cv, NULL);
+       pthread_mutex_init(&client_auth.mtx, NULL);
+
+       pthread_mutex_lock(&client_auth.mtx);
        client_auth.state = AUTH_INIT;
+       flag_sub(&interrupt, &client_auth.cv); // make sure Ctrl+C will work during AUTH_WAIT
 
-       while (! interrupt->done) {
-               switch (client_auth.state) {
-                       case AUTH_INIT:
-                               if (name_prompt())
-                                       break;
-                               else
-                                       return false;
-
-                       case AUTH_WAIT:
-                               sched_yield();
-                               break;
-
-                       case AUTH_SUCCESS:
-                               return true;
-               }
-       }
+       auth_loop();
+
+       flag_uns(&interrupt, &client_auth.cv);
+       bool success = client_auth.state == AUTH_SUCCESS;
+       pthread_mutex_unlock(&client_auth.mtx);
 
-       return false;
+       return success;
 }
 
 void client_auth_deinit()
 {
+       pthread_cond_destroy(&client_auth.cv);
+       pthread_mutex_destroy(&client_auth.mtx);
        linenoiseFree(client_auth.name);
 }
index 4d30ca0a28c299a02f141b1aceacbb9e2f2062cb..3d8d4e46016fa96b156c5ae57f4acd16b17122ae 100644 (file)
@@ -1,21 +1,22 @@
 #ifndef _CLIENT_AUTH_H_
 #define _CLIENT_AUTH_H_
 
-typedef enum
-{
+#include <pthread.h>
+
+typedef enum {
        AUTH_INIT,
        AUTH_WAIT,
        AUTH_SUCCESS,
 } ClientAuthState;
 
-extern volatile struct ClientAuth
-{
+extern struct ClientAuth {
        char *name;
        ClientAuthState state;
+       pthread_cond_t cv;
+       pthread_mutex_t mtx;
 } client_auth;
 
 bool client_auth_init();
-void client_auth_assert_state(ClientAuthState state, const char *pkt);
 void client_auth_deinit();
 
-#endif
+#endif // _CLIENT_AUTH_H_
index 2dbe24b07bef27f6aa0b8cbce9fdc210b1e4dfdc..be0d221c5abfa427fb3c155dbcc59933383ddcf8 100644 (file)
@@ -4,39 +4,46 @@
 struct ClientConfig client_config = {
        .antialiasing = 4,
        .mipmap = true,
-       .render_distance = 255.0,
+       .view_distance = 255.0,
        .vsync = true,
        .meshgen_threads = 4,
 };
 
+#define NUM_CONFIG_ENTRIES 5
+static ConfigEntry config_entries[NUM_CONFIG_ENTRIES] = {
+       {
+               .type = CONFIG_UINT,
+               .key = "antialiasing",
+               .value = &client_config.antialiasing,
+       },
+       {
+               .type = CONFIG_BOOL,
+               .key = "mipmap",
+               .value = &client_config.mipmap,
+       },
+       {
+               .type = CONFIG_FLOAT,
+               .key = "view_distance",
+               .value = &client_config.view_distance,
+       },
+       {
+               .type = CONFIG_BOOL,
+               .key = "vsync",
+               .value = &client_config.vsync,
+       },
+       {
+               .type = CONFIG_UINT,
+               .key = "meshgen_threads",
+               .value = &client_config.meshgen_threads,
+       },
+};
+
 __attribute__((constructor)) static void client_config_init()
 {
-       config_read("client.conf", (ConfigEntry[]) {
-               {
-                       .type = CT_UINT,
-                       .key = "antialiasing",
-                       .value = &client_config.antialiasing,
-               },
-               {
-                       .type = CT_BOOL,
-                       .key = "mipmap",
-                       .value = &client_config.mipmap,
-               },
-               {
-                       .type = CT_FLOAT,
-                       .key = "render_distance",
-                       .value = &client_config.render_distance,
-               },
-               {
-                       .type = CT_BOOL,
-                       .key = "vsync",
-                       .value = &client_config.vsync,
-               },
-               {
-                       .type = CT_UINT,
-                       .key = "meshgen_threads",
-                       .value = &client_config.meshgen_threads,
-               },
-       }, 5);
+       config_read("client.conf", config_entries, NUM_CONFIG_ENTRIES);
 }
 
+__attribute__((destructor)) static void client_config_deinit()
+{
+       config_free(config_entries, NUM_CONFIG_ENTRIES);
+}
index 4850e4548b4cd9339362300e3b53f0e3298489e0..fea57d26091f5951107e1f76ad9399abe81cbd3e 100644 (file)
@@ -6,7 +6,7 @@
 extern struct ClientConfig {
        unsigned int antialiasing;
        bool mipmap;
-       double render_distance;
+       double view_distance;
        bool vsync;
        unsigned int meshgen_threads;
 } client_config;
diff --git a/src/client/client_entity.c b/src/client/client_entity.c
new file mode 100644 (file)
index 0000000..2c34b68
--- /dev/null
@@ -0,0 +1,175 @@
+#include <dragonstd/map.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "client/client_entity.h"
+#include "client/client_player.h"
+
+ClientEntityType client_entity_types[COUNT_ENTITY];
+
+static Map entities;
+
+// any thread
+// called when adding, getting or removing an entity from the map
+static int cmp_entity(const Refcount *entity, const u64 *id)
+{
+       return u64_cmp(&((ClientEntity *) entity->obj)->data.id, id);
+}
+
+// recv thread
+// called when server sent removal of entity
+static void entity_drop(ClientEntity *entity)
+{
+       if (entity->type->remove)
+               entity->type->remove(entity);
+
+       refcount_drp(&entity->rc);
+}
+
+// any thread
+// called when all refs have been dropped
+static void entity_delete(ClientEntity *entity)
+{
+       if (entity->type->free)
+               entity->type->free(entity);
+
+       refcount_dst(&entity->rc);
+
+       if (entity->data.nametag)
+               free(entity->data.nametag);
+
+       pthread_rwlock_init(&entity->lock_pos_rot, NULL);
+       pthread_rwlock_init(&entity->lock_box_eye, NULL);
+       pthread_rwlock_init(&entity->lock_nametag, NULL);
+
+       free(entity);
+}
+
+// main thread
+// called on startup
+void client_entity_init()
+{
+       map_ini(&entities);
+}
+
+// main thead
+// called on shutdown
+void client_entity_deinit()
+{
+       // forget all entities
+       map_cnl(&entities, &refcount_drp, NULL, NULL, 0);
+}
+
+ClientEntity *client_entity_grab(u64 id)
+{
+       return id ? map_get(&entities, &id, &cmp_entity, &refcount_grb) : NULL;
+}
+
+void client_entity_transform(ClientEntity *entity)
+{
+       if (!entity->model)
+               return;
+
+       entity->model->root->pos = (v3f32) {entity->data.pos.x, entity->data.pos.y, entity->data.pos.z};
+       entity->model->root->rot = (v3f32) {entity->data.rot.x, entity->data.rot.y, entity->data.rot.z};
+
+       if (entity->type->transform)
+               entity->type->transform(entity);
+
+       model_node_transform(entity->model->root);
+}
+
+void client_entity_add(__attribute__((unused)) DragonnetPeer *peer, ToClientEntityAdd *pkt)
+{
+       if (pkt->type >= COUNT_ENTITY)
+               return;
+
+       ClientEntity *entity = malloc(sizeof *entity);
+
+       entity->data = pkt->data;
+       entity->type = &client_entity_types[pkt->type];
+       refcount_ini(&entity->rc, entity, &entity_delete);
+
+       pkt->data.nametag = NULL;
+
+       entity->model = NULL;
+
+       pthread_rwlock_init(&entity->lock_pos_rot, NULL);
+       pthread_rwlock_init(&entity->lock_box_eye, NULL);
+       pthread_rwlock_init(&entity->lock_nametag, NULL);
+
+       if (entity->type->add)
+               entity->type->add(entity);
+
+       if (!map_add(&entities, &entity->data.id, &entity->rc, &cmp_entity, &refcount_inc))
+               fprintf(stderr, "[warning] failed to add entity %lu\n", entity->data.id);
+
+       refcount_drp(&entity->rc);
+}
+
+void client_entity_remove(__attribute__((unused)) DragonnetPeer *peer, ToClientEntityRemove *pkt)
+{
+       map_del(&entities, &pkt->id, &cmp_entity, &entity_drop, NULL, &refcount_obj);
+}
+
+void client_entity_update_pos_rot(__attribute__((unused)) DragonnetPeer *peer, ToClientEntityUpdatePosRot *pkt)
+{
+       ClientEntity *entity = client_entity_grab(pkt->id);
+
+       if (!entity)
+               return;
+
+       pthread_rwlock_wrlock(&entity->lock_pos_rot);
+
+       entity->data.pos = pkt->pos;
+       entity->data.rot = pkt->rot;
+
+       if (entity->type->update_pos_rot)
+               entity->type->update_pos_rot(entity);
+
+       pthread_rwlock_unlock(&entity->lock_pos_rot);
+
+       refcount_drp(&entity->rc);
+}
+
+void client_entity_update_box_eye(__attribute__((unused)) DragonnetPeer *peer, ToClientEntityUpdateBoxEye *pkt)
+{
+       ClientEntity *entity = client_entity_grab(pkt->id);
+
+       if (!entity)
+               return;
+
+       pthread_rwlock_wrlock(&entity->lock_box_eye);
+
+       entity->data.box = pkt->box;
+       entity->data.eye = pkt->eye;
+
+       if (entity->type->update_box_eye)
+               entity->type->update_box_eye(entity);
+
+       pthread_rwlock_unlock(&entity->lock_box_eye);
+
+       refcount_drp(&entity->rc);
+}
+
+void client_entity_update_nametag(__attribute__((unused)) DragonnetPeer *peer, ToClientEntityUpdateNametag *pkt)
+{
+       ClientEntity *entity = client_entity_grab(pkt->id);
+
+       if (!entity)
+               return;
+
+       pthread_rwlock_wrlock(&entity->lock_nametag);
+
+       if (entity->data.nametag)
+               free(entity->data.nametag);
+
+       entity->data.nametag = pkt->nametag;
+       pkt->nametag = NULL;
+
+       if (entity->type->update_nametag)
+               entity->type->update_nametag(entity);
+
+       pthread_rwlock_unlock(&entity->lock_nametag);
+
+       refcount_drp(&entity->rc);
+}
diff --git a/src/client/client_entity.h b/src/client/client_entity.h
new file mode 100644 (file)
index 0000000..05651c4
--- /dev/null
@@ -0,0 +1,51 @@
+#ifndef _CLIENT_ENTITY_H_
+#define _CLIENT_ENTITY_H_
+
+#include <dragonnet/peer.h>
+#include <dragonstd/refcount.h>
+#include <pthread.h>
+#include "client/model.h"
+#include "entity.h"
+#include "types.h"
+
+typedef struct {
+       EntityData data;
+       struct ClientEntityType *type;
+       Refcount rc;
+
+       Model *model;
+
+       pthread_rwlock_t lock_pos_rot;
+       pthread_rwlock_t lock_box_eye;
+       pthread_rwlock_t lock_nametag;
+} ClientEntity;
+
+typedef struct ClientEntityType {
+       void (*add   )(ClientEntity *entity); // called when server sent addition of entity
+       void (*remove)(ClientEntity *entity); // called when server sent removal of entity
+       void (*free  )(ClientEntity *entity); // called when entity is garbage collected
+
+       void (*update_pos_rot)(ClientEntity *entity);
+       void (*update_box_eye)(ClientEntity *entity);
+       void (*update_nametag)(ClientEntity *entity);
+
+       void (*transform)(ClientEntity *entity);
+} ClientEntityType;
+
+void client_entity_init();
+void client_entity_deinit();
+
+ClientEntity *client_entity_grab(u64 id);
+void client_entity_drop(ClientEntity *entity);
+
+void client_entity_transform(ClientEntity *entity);
+
+void client_entity_add(DragonnetPeer *peer, ToClientEntityAdd *pkt);
+void client_entity_remove(DragonnetPeer *peer, ToClientEntityRemove *pkt);
+void client_entity_update_pos_rot(DragonnetPeer *peer, ToClientEntityUpdatePosRot *pkt);
+void client_entity_update_box_eye(DragonnetPeer *peer, ToClientEntityUpdateBoxEye *pkt);
+void client_entity_update_nametag(DragonnetPeer *peer, ToClientEntityUpdateNametag *pkt);
+
+extern ClientEntityType client_entity_types[];
+
+#endif // _CLIENT_ENTITY_H_
diff --git a/src/client/client_map.c b/src/client/client_map.c
deleted file mode 100644 (file)
index 43252dd..0000000
+++ /dev/null
@@ -1,253 +0,0 @@
-#include <stdio.h>
-#include <stdlib.h>
-#include "client/blockmesh.h"
-#include "client/facecache.h"
-#include "client/client_config.h"
-#include "client/client_map.h"
-#include "client/client_player.h"
-#include "client/debug_menu.h"
-#include "util.h"
-#define MAX_BLOCK_REQUESTS 4
-
-struct ClientMap client_map;
-
-// meshgen functions
-
-// dequeue callback to thread-safely update
-static void set_dequeued(MapBlock *block)
-{
-       pthread_mutex_lock(&block->mtx);
-       ((MapBlockExtraData *) block->extra)->queue = false;
-       pthread_mutex_unlock(&block->mtx);
-}
-
-// mesh generator step
-static void meshgen_step()
-{
-       MapBlock *block = queue_dequeue_callback(client_map.queue, (void *) &set_dequeued);
-
-       if (block)
-               blockmesh_make(block);
-}
-
-// pthread start routine for meshgen thread
-static void *meshgen_thread(unused void *arg)
-{
-       while (! client_map.cancel)
-               meshgen_step();
-
-       return NULL;
-}
-
-// sync functions
-
-// send block request command to server
-static void request_position(v3s32 pos)
-{
-       dragonnet_peer_send_ToServerRequestBlock(client, &(ToServerRequestBlock) {
-               .pos = pos
-       });
-}
-
-// mapblock synchronisation step
-static void sync_step()
-{
-       static u64 tick = 0;
-       static v3s32 *old_requested_positions = NULL;
-       static size_t old_requested_positions_count = 0;
-
-       u64 last_tick = tick++;
-
-       v3f64 player_pos = client_player_get_position();
-       v3s32 center = map_node_to_block_pos((v3s32) {player_pos.x, player_pos.y, player_pos.z}, NULL);
-
-       v3s32 *requested_positions = malloc(sizeof(v3s32) * MAX_BLOCK_REQUESTS);
-       size_t requested_positions_count = 0;
-
-       for (size_t i = 0; i < client_map.blocks_count; i++) {
-               v3s32 pos = facecache_face(i, &center);
-               MapBlock *block = map_get_block(client_map.map, pos, false);
-
-               if (block) {
-                       pthread_mutex_lock(&block->mtx);
-                       MapBlockExtraData *extra = block->extra;
-
-                       switch (extra->state) {
-                               case MBS_READY:
-                                       if (extra->last_synced < last_tick)
-                                               request_position(pos);
-                                       fallthrough;
-
-                               case MBS_FRESH:
-                                       extra->state = MBS_READY;
-                                       extra->last_synced = tick;
-                                       break;
-
-                               case MBS_RECIEVING:
-                                       break;
-                       }
-                       pthread_mutex_unlock(&block->mtx);
-               } else if (requested_positions_count < MAX_BLOCK_REQUESTS) {
-                       bool should_request = true;
-
-                       for (size_t i = 0; i < old_requested_positions_count; i++) {
-                               if (v3s32_equals(old_requested_positions[i], pos)) {
-                                       should_request = false;
-                                       break;
-                               }
-                       }
-
-                       if (should_request)
-                               request_position(pos);
-
-                       requested_positions[requested_positions_count++] = pos;
-               }
-       }
-
-       if (old_requested_positions)
-               free(old_requested_positions);
-
-       old_requested_positions = requested_positions;
-       old_requested_positions_count = requested_positions_count;
-}
-
-// pthread start routine for sync thread
-static void *sync_thread(unused void *arg)
-{
-       while (! client_map.cancel)
-               sync_step();
-
-       return NULL;
-}
-
-// map callbacks
-// note: all these functions require the block mutex to be locked, which is always the case when a map callback is invoked
-
-// callback for initializing a newly created block
-// allocate and initialize extra data
-static void on_create_block(MapBlock *block)
-{
-       MapBlockExtraData *extra = block->extra = malloc(sizeof(MapBlockExtraData));
-
-       extra->state = MBS_RECIEVING;
-       extra->queue = false;
-       extra->last_synced = 0;
-       extra->obj = NULL;
-       extra->all_air = false;
-}
-
-// callback for deleting a block
-// free extra data
-static void on_delete_block(MapBlock *block)
-{
-       free(block->extra);
-}
-
-// callback for determining whether a block should be returned by map_get_block
-// hold back blocks that have not been fully read from server yet when the create flag is set to true
-static bool on_get_block(MapBlock *block, bool create)
-{
-       return create || ((MapBlockExtraData *) block->extra)->state > MBS_RECIEVING;
-}
-
-// public functions
-
-// ClientMap singleton constructor
-void client_map_init()
-{
-       client_map.map = map_create((MapCallbacks) {
-               .create_block = &on_create_block,
-               .delete_block = &on_delete_block,
-               .get_block = &on_get_block,
-               .set_node = NULL,
-               .after_set_node = NULL,
-       });
-       client_map.queue = queue_create();
-       client_map.cancel = false;
-       client_map.sync_thread = 0;
-       client_map_set_simulation_distance(10);
-
-       client_map.meshgen_threads = malloc(sizeof *client_map.meshgen_threads * client_config.meshgen_threads);
-       for (unsigned int i = 0; i < client_config.meshgen_threads; i++)
-               client_map.meshgen_threads[i] = 0; // why
-}
-
-// ClientMap singleton destructor
-void client_map_deinit()
-{
-       queue_delete(client_map.queue);
-       map_delete(client_map.map);
-}
-
-// start meshgen and sync threads
-void client_map_start()
-{
-       for (unsigned int i = 0; i < client_config.meshgen_threads; i++)
-               pthread_create(&client_map.meshgen_threads[i], NULL, &meshgen_thread, NULL);
-
-       pthread_create(&client_map.sync_thread, NULL, &sync_thread, NULL);
-}
-
-// stop meshgen and sync threads
-void client_map_stop()
-{
-       client_map.cancel = true;
-       queue_cancel(client_map.queue);
-
-       for (unsigned int i = 0; i < client_config.meshgen_threads; i++)
-               if (client_map.meshgen_threads[i])
-                       pthread_join(client_map.meshgen_threads[i], NULL);
-       free(client_map.meshgen_threads);
-
-       if (client_map.sync_thread)
-               pthread_join(client_map.sync_thread, NULL);
-}
-
-// update simulation distance
-void client_map_set_simulation_distance(u32 simulation_distance)
-{
-       client_map.simulation_distance = simulation_distance;
-       client_map.blocks_count = facecache_count(simulation_distance);
-}
-
-// called when a block was actually recieved from server
-void client_map_block_received(MapBlock *block)
-{
-       pthread_mutex_lock(&block->mtx);
-       MapBlockExtraData *extra = block->extra;
-       if (extra->state == MBS_RECIEVING)
-               extra->state = MBS_FRESH;
-       pthread_mutex_unlock(&block->mtx);
-
-       client_map_schedule_update_block_mesh(block);
-
-       client_map_schedule_update_block_mesh(map_get_block(client_map.map, (v3s32) {block->pos.x + 1, block->pos.y + 0, block->pos.z + 0}, false));
-       client_map_schedule_update_block_mesh(map_get_block(client_map.map, (v3s32) {block->pos.x + 0, block->pos.y + 1, block->pos.z + 0}, false));
-       client_map_schedule_update_block_mesh(map_get_block(client_map.map, (v3s32) {block->pos.x + 0, block->pos.y + 0, block->pos.z + 1}, false));
-       client_map_schedule_update_block_mesh(map_get_block(client_map.map, (v3s32) {block->pos.x - 1, block->pos.y - 0, block->pos.z - 0}, false));
-       client_map_schedule_update_block_mesh(map_get_block(client_map.map, (v3s32) {block->pos.x - 0, block->pos.y - 1, block->pos.z - 0}, false));
-       client_map_schedule_update_block_mesh(map_get_block(client_map.map, (v3s32) {block->pos.x - 0, block->pos.y - 0, block->pos.z - 1}, false));
-}
-
-// enqueue block to mesh update queue
-void client_map_schedule_update_block_mesh(MapBlock *block)
-{
-       if (! block)
-               return;
-
-       pthread_mutex_lock(&block->mtx);
-       MapBlockExtraData *extra = block->extra;
-
-       if (! extra->queue) {
-               if (extra->all_air) {
-                       if (extra->obj) {
-                               extra->obj->remove = true;
-                               extra->obj = NULL;
-                       }
-               } else {
-                       extra->queue = true;
-                       queue_enqueue(client_map.queue, block);
-               }
-       }
-       pthread_mutex_unlock(&block->mtx);
-}
diff --git a/src/client/client_map.h b/src/client/client_map.h
deleted file mode 100644 (file)
index b49f199..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-#ifndef _CLIENT_MAP_H_
-#define _CLIENT_MAP_H_
-
-#include <stdbool.h>
-#include <pthread.h>
-#include <dragonstd/queue.h>
-#include "map.h"
-#include "client/object.h"
-
-typedef enum
-{
-       MBS_RECIEVING, // currently deserializing
-       MBS_FRESH,     // first deserialisation finished, not processed by sync thread yet
-       MBS_READY,     // ready to use and processed by sync thread
-} MapBlockState;
-
-typedef struct
-{
-       MapBlockState state; // keep track of the deserialisation and sync processing state
-       bool queue;          // whether the block is in meshgen queue
-       u64 last_synced;     // keep track of when a block was synced the last time (used to detect when a block got out of and then back into range)
-       Object *obj;         // mesh object, generated by blockmesh file
-       bool all_air;        // no thoughts brain empty
-} MapBlockExtraData;
-
-extern struct ClientMap
-{
-       Map *map;                   // map object
-       Queue *queue;               // MapBlock * queue (thread safe)
-       atomic_bool cancel;         // used to notify meshgen and sync thread about quit
-       pthread_t *meshgen_threads; // consumer threads for meshgen queue
-       pthread_t sync_thread;      // this thread requests new / changed blocks from server
-       u32 simulation_distance;    // simulation distance sent by server
-       size_t blocks_count;        // cached number of facecache positions to process every sync step (matches simulation distance)
-} client_map;
-
-void client_map_init();                                           // ClientMap singleton constructor
-void client_map_deinit();                                         // ClientMap singleton destructor
-void client_map_set_simulation_distance(u32 simulation_distance); // update simulation distance
-void client_map_start();                                          // start meshgen and sync threads
-void client_map_stop();                                           // stop meshgen and sync threads
-void client_map_block_received(MapBlock *block);                  // called when a block was actually recieved from server
-void client_map_schedule_update_block_mesh(MapBlock *block);      // enqueue block to mesh update queue
-
-#endif
index 18283ebbdc69495cdd9696622741904efd1edd65..970059074e5bf7338a20fbe633d90abff8c0d6d4 100644 (file)
 #include "client/client.h"
 #include "client/client_node.h"
+#include "color.h"
 #include "environment.h"
 #include "node.h"
 #include "perlin.h"
-#include "util.h"
+
 #define TILES_SIMPLE(path) {.paths = {path, NULL, NULL, NULL, NULL, NULL}, .indices = {0, 0, 0, 0, 0, 0}, .textures = {NULL}}
 #define TILES_NONE {.paths = {NULL}, .indices = {0}, .textures = {NULL}}
 
-static f32 hue_to_rgb(f32 p, f32 q, f32 t)
-{
-    if (t < 0.0f)
-        t += 1.0f;
-
-    if (t > 1.0f)
-        t -= 1.0f;
-
-    if (t < 1.0f / 6.0f)
-        return p + (q - p) * 6.0f * t;
-
-    if (t < 1.0f / 2.0f)
-        return q;
-
-    if (t < 2.0f / 3.0f)
-        return p + (q - p) * (2.0f / 3.0f - t) * 6.0f;
-
-    return p;
-}
-
-static Vertex3DColor hsl_to_rgb(v3f32 hsl)
+static void render_grass(NodeArgsRender *args)
 {
-       Vertex3DColor rgb;
-
-    if (hsl.y == 0.0f) {
-               rgb = (Vertex3DColor) {hsl.z, hsl.z, hsl.z};
-    } else {
-        f32 q = hsl.z < 0.5f ? hsl.z * (1.0f + hsl.y) : hsl.z + hsl.y - hsl.z * hsl.y;
-        f32 p = 2.0f * hsl.z - q;
-
-        rgb.r = hue_to_rgb(p, q, hsl.x + 1.0f / 3.0f);
-        rgb.g = hue_to_rgb(p, q, hsl.x);
-        rgb.b = hue_to_rgb(p, q, hsl.x - 1.0f / 3.0f);
-    }
-
-    return rgb;
-}
-
-static void render_grass(v3s32 pos, unused MapNode *node, Vertex3D *vertex, unused int f, unused int v)
-{
-       f32 hum_min, hum_max, temp_max;
-       hum_min = 0.13f;
-       hum_max = 0.33f;
-       temp_max = 0.45f;
-
-       f32 temp_f = f64_clamp(0.3f - get_temperature(pos), 0.0f, 0.3f) / 0.3f;
-
-       vertex->color = hsl_to_rgb((v3f32) {(get_humidity(pos) * (hum_max - hum_min) + hum_min) * (1.0f - temp_f) + temp_max * temp_f, 1.0f, 0.5f});
+       args->vertex.color = hsl_to_rgb((v3f32) {f32_mix(
+               // hue values between .13 and .33 depending on humidity
+               f32_mix(
+                       0.13f,
+                       0.33f,
+                       get_humidity(args->pos)
+               ),
+               // move towards .45 while temperature is between .3 and .0
+               0.45f,
+               f32_clamp(
+                       0.3f - get_temperature(args->pos),
+                       0.0f,
+                       0.3f
+               ) / 0.3f
+       ), 1.0f, 0.5f});
 }
 
-static void render_stone(v3s32 pos, unused MapNode *node, Vertex3D *vertex, unused int f, unused int v)
+static void render_stone(NodeArgsRender *args)
 {
-       vertex->textureCoordinates.s += noise2d(pos.x, pos.z, 0, seed + SO_TEXTURE_OFFSET_S);
-       vertex->textureCoordinates.t += noise2d(pos.x, pos.z, 0, seed + SO_TEXTURE_OFFSET_T);
+       args->vertex.cube.textureCoordinates.x += noise2d(args->pos.x, args->pos.z, 0, seed + SO_TEXTURE_OFFSET_S);
+       args->vertex.cube.textureCoordinates.y += noise2d(args->pos.x, args->pos.z, 0, seed + SO_TEXTURE_OFFSET_T);
 }
 
-static void render_hsl(unused v3s32 pos, MapNode *node, Vertex3D *vertex, unused int f, unused int v)
+static void render_color(NodeArgsRender *args)
 {
-       vertex->color = hsl_to_rgb(((HSLData *) node->data)->color);
+       args->vertex.color = ((ColorData *) args->node->data)->color;
 }
 
 ClientNodeDefinition client_node_definitions[NODE_UNLOADED] = {
        // unknown
        {
                .tiles = TILES_SIMPLE(RESSOURCE_PATH "textures/unknown.png"),
-               .visibility = NV_SOLID,
+               .visibility = VISIBILITY_SOLID,
                .mipmap = true,
                .render = NULL,
        },
        // air
        {
                .tiles = TILES_NONE,
-               .visibility = NV_NONE,
+               .visibility = VISIBILITY_NONE,
                .mipmap = true,
                .render = NULL,
        },
        // grass
        {
                .tiles = TILES_SIMPLE(RESSOURCE_PATH "textures/grass.png"),
-               .visibility = NV_SOLID,
+               .visibility = VISIBILITY_SOLID,
                .mipmap = true,
                .render = &render_grass,
        },
        // dirt
        {
                .tiles = TILES_SIMPLE(RESSOURCE_PATH "textures/dirt.png"),
-               .visibility = NV_SOLID,
+               .visibility = VISIBILITY_SOLID,
                .mipmap = true,
                .render = NULL,
        },
        // stone
        {
                .tiles = TILES_SIMPLE(RESSOURCE_PATH "textures/stone.png"),
-               .visibility = NV_SOLID,
+               .visibility = VISIBILITY_SOLID,
                .mipmap = true,
                .render = &render_stone,
        },
        // snow
        {
                .tiles = TILES_SIMPLE(RESSOURCE_PATH "textures/snow.png"),
-               .visibility = NV_SOLID,
+               .visibility = VISIBILITY_SOLID,
                .mipmap = true,
                .render = NULL,
        },
@@ -118,16 +88,16 @@ ClientNodeDefinition client_node_definitions[NODE_UNLOADED] = {
                        .indices = {0, 0, 0, 0, 1, 1},
                        .textures = {NULL},
                },
-               .visibility = NV_SOLID,
+               .visibility = VISIBILITY_SOLID,
                .mipmap = true,
-               .render = &render_hsl,
+               .render = &render_color,
        },
        // oak leaves
        {
                .tiles = TILES_SIMPLE(RESSOURCE_PATH "textures/oak_leaves.png"),
-               .visibility = NV_SOLID,
+               .visibility = VISIBILITY_SOLID,
                .mipmap = true,
-               .render = &render_hsl,
+               .render = &render_color,
        },
        // pine wood
        {
@@ -136,16 +106,16 @@ ClientNodeDefinition client_node_definitions[NODE_UNLOADED] = {
                        .indices = {0, 0, 0, 0, 1, 1},
                        .textures = {NULL},
                },
-               .visibility = NV_SOLID,
+               .visibility = VISIBILITY_SOLID,
                .mipmap = true,
-               .render = &render_hsl,
+               .render = &render_color,
        },
        // pine leaves
        {
                .tiles = TILES_SIMPLE(RESSOURCE_PATH "textures/pine_leaves.png"),
-               .visibility = NV_CLIP,
+               .visibility = VISIBILITY_CLIP,
                .mipmap = true,
-               .render = &render_hsl,
+               .render = &render_color,
        },
        // palm wood
        {
@@ -154,42 +124,42 @@ ClientNodeDefinition client_node_definitions[NODE_UNLOADED] = {
                        .indices = {0, 0, 0, 0, 1, 1},
                        .textures = {NULL},
                },
-               .visibility = NV_SOLID,
+               .visibility = VISIBILITY_SOLID,
                .mipmap = true,
-               .render = &render_hsl,
+               .render = &render_color,
        },
        // palm leaves
        {
                .tiles = TILES_SIMPLE(RESSOURCE_PATH "textures/palm_leaves.png"),
-               .visibility = NV_SOLID,
+               .visibility = VISIBILITY_SOLID,
                .mipmap = true,
-               .render = &render_hsl,
+               .render = &render_color,
        },
        // sand
        {
                .tiles = TILES_SIMPLE(RESSOURCE_PATH "textures/sand.png"),
-               .visibility = NV_SOLID,
+               .visibility = VISIBILITY_SOLID,
                .mipmap = true,
                .render = NULL,
        },
        // water
        {
                .tiles = TILES_SIMPLE(RESSOURCE_PATH "textures/water.png"),
-               .visibility = NV_BLEND,
+               .visibility = VISIBILITY_BLEND,
                .mipmap = true,
                .render = NULL,
        },
        // lava
        {
                .tiles = TILES_SIMPLE(RESSOURCE_PATH "textures/lava.png"),
-               .visibility = NV_BLEND,
+               .visibility = VISIBILITY_BLEND,
                .mipmap = true,
                .render = NULL,
        },
        // vulcano_stone
        {
                .tiles = TILES_SIMPLE(RESSOURCE_PATH "textures/vulcano_stone.png"),
-               .visibility = NV_SOLID,
+               .visibility = VISIBILITY_SOLID,
                .mipmap = true,
                .render = NULL,
        },
@@ -197,10 +167,10 @@ ClientNodeDefinition client_node_definitions[NODE_UNLOADED] = {
 
 void client_node_init()
 {
-       for (Node node = NODE_UNKNOWN; node < NODE_UNLOADED; node++) {
+       for (NodeType node = NODE_UNKNOWN; node < NODE_UNLOADED; node++) {
                ClientNodeDefinition *def = &client_node_definitions[node];
 
-               if (def->visibility != NV_NONE) {
+               if (def->visibility != VISIBILITY_NONE) {
                        Texture *textures[6];
 
                        for (int i = 0; i < 6; i++) {
index bb145f6a47a36588538a14b7f3bc45e346b27e33..a922df486fe63ab78ee103eb2cf042469a1dd0cc 100644 (file)
@@ -1,32 +1,36 @@
 #ifndef _CLIENT_NODE_H_
 #define _CLIENT_NODE_H_
 
-#include "client/object.h"
+#include "client/terrain_gfx.h"
 #include "client/texture.h"
-#include "map.h"
+#include "terrain.h"
 
-typedef enum
-{
-       NV_NONE,
-       NV_CLIP,
-       NV_BLEND,
-       NV_SOLID,
+typedef enum {
+       VISIBILITY_NONE,
+       VISIBILITY_CLIP,
+       VISIBILITY_BLEND,
+       VISIBILITY_SOLID,
 } NodeVisibility;
 
-typedef struct
-{
-       struct
-       {
+typedef struct {
+       v3s32 pos;
+       TerrainNode *node;
+       TerrainVertex vertex;
+       unsigned int f, v;
+} NodeArgsRender;
+
+typedef struct {
+       struct {
                char *paths[6];       // input
                int indices[6];       // input
                Texture *textures[6]; // output
        } tiles;
        NodeVisibility visibility;
        bool mipmap;
-       void (*render)(v3s32 pos, MapNode *node, Vertex3D *vertex, int f, int v);
+       void (*render)(NodeArgsRender *args);
 } ClientNodeDefinition;
 
 extern ClientNodeDefinition client_node_definitions[];
 void client_node_init();
 
-#endif
+#endif // _CLIENT_NODE_H_
index a5a69c1d3e04bcc8fa6a1001660f8e80c836458d..08b9adb10bf5c070f1c498082ea7aea24cf6129c 100644 (file)
 #include <stdio.h>
-#include "environment.h"
+#include <stdlib.h>
 #include "client/camera.h"
 #include "client/client.h"
-#include "client/client_map.h"
 #include "client/client_player.h"
+#include "client/client_terrain.h"
 #include "client/cube.h"
 #include "client/debug_menu.h"
 #include "client/texture.h"
+#include "environment.h"
+#include "physics.h"
 
 struct ClientPlayer client_player;
 
-// to be called whenever the player position changes
-// rwlock has to be read or write locked
+static ClientEntity *player_entity;
+static pthread_rwlock_t lock_player_entity;
+
+// updat epos/rot box/eye functions
+
+static void update_eye_pos_camera()
+{
+       v3f64 pos = player_entity->data.pos;
+       v3f32 eye = player_entity->data.eye;
+
+       camera_set_position((v3f32) {pos.x + eye.x, pos.y + eye.y, pos.z + eye.z});
+}
+
 static void update_pos()
 {
-       camera_set_position((v3f32) {client_player.pos.x, client_player.pos.y + client_player.eye_height, client_player.pos.z});
-       dragonnet_peer_send_ToServerPos(client, &(ToServerPos) {
-               .pos = client_player.pos,
-       });
+       pthread_rwlock_rdlock(&player_entity->lock_box_eye);
+       update_eye_pos_camera();
+       pthread_rwlock_unlock(&player_entity->lock_box_eye);
 
-       client_player.obj->pos = (v3f32) {client_player.pos.x, client_player.pos.y, client_player.pos.z};
-       object_transform(client_player.obj);
+       debug_menu_changed(ENTRY_POS);
+       debug_menu_changed(ENTRY_HUMIDITY);
+       debug_menu_changed(ENTRY_TEMPERATURE);
+}
 
-       debug_menu_update_pos();
-       debug_menu_update_humidity();
-       debug_menu_update_temperature();
+static void update_rot()
+{
+       camera_set_angle(player_entity->data.rot.x, player_entity->data.rot.y);
+       debug_menu_changed(ENTRY_YAW);
+       debug_menu_changed(ENTRY_PITCH);
 }
 
-// get absolute player bounding box
-// rwlock has to be read- or write locked
-static aabb3f64 get_box()
+static void update_transform()
 {
-       return (aabb3f64) {
-               {client_player.box.min.x + client_player.pos.x, client_player.box.min.y + client_player.pos.y, client_player.box.min.z + client_player.pos.z},
-               {client_player.box.max.x + client_player.pos.x, client_player.box.max.y + client_player.pos.y, client_player.box.max.z + client_player.pos.z},
-       };
+       client_entity_transform(player_entity);
 }
 
-// get absolute integer box that contains all nodes a float bounding box touches
-static aabb3s32 round_box(aabb3f64 box)
+static void send_pos_rot()
 {
-       return (aabb3s32) {
-               {floor(box.min.x + 0.5), floor(box.min.y + 0.5), floor(box.min.z + 0.5)},
-               {ceil(box.max.x - 0.5), ceil(box.max.y - 0.5), ceil(box.max.z - 0.5)},
-       };
+       dragonnet_peer_send_ToServerPosRot(client, &(ToServerPosRot) {
+               .pos = player_entity->data.pos,
+               .rot = player_entity->data.rot,
+       });
+
+       update_transform();
 }
 
-// return true if node at x, y, z is solid (or unloaded)
-static bool is_solid(s32 x, s32 y, s32 z)
+static void recv_pos_rot()
 {
-       Node node = map_get_node(client_map.map, (v3s32) {x, y, z}).type;
-       return node == NODE_UNLOADED || node_definitions[node].solid;
+       update_pos();
+       update_rot();
+
+       update_transform();
 }
 
-// determine if player can jump currently (must be standing on a solid block)
-// rwlock has to be read- or write locked
-static bool can_jump()
+// entity callbacks
+
+static void on_add(ClientEntity *entity)
 {
-       if (client_player.velocity.y != 0.0)
-               return false;
+       pthread_rwlock_wrlock(&lock_player_entity);
 
-       aabb3f64 fbox = get_box();
-       fbox.min.y -= 0.5;
+       if (player_entity) {
+               fprintf(stderr, "[error] attempt to re-add localplayer entity\n");
+               exit(EXIT_FAILURE);
+       } else {
+               player_entity = refcount_grb(&entity->rc);
+               recv_pos_rot();
 
-       aabb3s32 box = round_box(fbox);
+               entity->type->update_nametag(entity);
+       }
 
-       if (fbox.min.y - (f64) box.min.y > 0.01)
-               return false;
+       pthread_rwlock_unlock(&lock_player_entity);
+}
 
-       for (s32 x = box.min.x; x <= box.max.x; x++)
-               for (s32 z = box.min.z; z <= box.max.z; z++)
-                       if (is_solid(x, box.min.y, z))
-                               return true;
+static void on_remove(ClientEntity *entity)
+{
+       pthread_rwlock_wrlock(&lock_player_entity);
+       refcount_drp(&entity->rc);
+       player_entity = NULL;
+       pthread_rwlock_unlock(&lock_player_entity);
+}
+
+static void on_update_pos_rot(__attribute__((unused)) ClientEntity *entity)
+{
+       recv_pos_rot();
+}
 
-       return false;
+static void on_update_box_eye(__attribute__((unused)) ClientEntity *entity)
+{
+       pthread_rwlock_rdlock(&lock_player_entity);
+       update_eye_pos_camera();
+       pthread_rwlock_unlock(&lock_player_entity);
+}
+
+static void on_update_nametag(ClientEntity *entity)
+{
+       if (entity->data.nametag) {
+               free(entity->data.nametag);
+               entity->data.nametag = NULL;
+       }
 }
 
-// ClientPlayer singleton constructor
+static void on_transform(ClientEntity *entity)
+{
+       entity->model->root->rot.y = entity->model->root->rot.z = 0.0f;
+}
+
+// called on startup
 void client_player_init()
 {
-       client_player.pos = (v3f64) {0.0, 0.0, 0.0};
-       client_player.velocity = (v3f64) {0.0, 0.0, 0.0};
-       client_player.box = (aabb3f64) {{-0.3, 0.0, -0.3}, {0.3, 1.75, 0.3}};
-       client_player.yaw = client_player.pitch = 0.0f;
-       client_player.eye_height = 1.5;
-       client_player.fly = false;
-       client_player.collision = true;
-       pthread_rwlock_init(&client_player.rwlock, NULL);
+       client_player.movement = (ToClientMovement) {
+               .flight = false,
+               .collision = true,
+               .speed = 0.0f,
+               .jump = 0.0f,
+               .gravity = 0.0f,
+       };
+
+       client_entity_types[ENTITY_LOCALPLAYER] = (ClientEntityType) {
+               .add = &on_add,
+               .remove = &on_remove,
+               .free = NULL,
+               .update_pos_rot = &on_update_pos_rot,
+               .update_box_eye = &on_update_box_eye,
+               .update_nametag = &on_update_nametag,
+               .transform = &on_transform,
+       };
+
+       client_entity_types[ENTITY_PLAYER] = (ClientEntityType) {
+               .add = NULL,
+               .remove = NULL,
+               .free = NULL,
+               .update_pos_rot = NULL,
+               .update_box_eye = NULL,
+               .update_nametag = NULL,
+               .transform = &on_transform,
+       };
+
+       pthread_rwlock_init(&client_player.lock_movement, NULL);
+
+       player_entity = NULL;
+       pthread_rwlock_init(&lock_player_entity, NULL);
 }
 
-// ClientPlayer singleton destructor
+// called on shutdown
 void client_player_deinit()
 {
-       pthread_rwlock_destroy(&client_player.rwlock);
+       pthread_rwlock_destroy(&client_player.lock_movement);
+       pthread_rwlock_destroy(&lock_player_entity);
+}
+
+ClientEntity *client_player_entity()
+{
+       ClientEntity *entity = NULL;
+
+       pthread_rwlock_rdlock(&lock_player_entity);
+       if (player_entity)
+               entity = refcount_grb(&player_entity->rc);
+       pthread_rwlock_unlock(&lock_player_entity);
+
+       return entity;
+}
+
+void client_player_update_pos(ClientEntity *entity)
+{
+       pthread_rwlock_rdlock(&lock_player_entity);
+
+       if (entity == player_entity) {
+               update_pos();
+               send_pos_rot();
+       }
+
+       pthread_rwlock_unlock(&lock_player_entity);
+}
+
+void client_player_update_rot(ClientEntity *entity)
+{
+       pthread_rwlock_rdlock(&lock_player_entity);
+
+       if (entity == player_entity) {
+               update_rot();
+               send_pos_rot();
+       }
+
+       pthread_rwlock_unlock(&lock_player_entity);
 }
 
+/*
 // create mesh object and info hud
 void client_player_add_to_scene()
 {
@@ -119,96 +223,62 @@ void client_player_add_to_scene()
        debug_menu_update_yaw();
        debug_menu_update_pitch();
 }
+*/
 
 // jump if possible
 void client_player_jump()
 {
-       pthread_rwlock_wrlock(&client_player.rwlock);
-       if (can_jump())
-               client_player.velocity.y += 10.0;
-       pthread_rwlock_unlock(&client_player.rwlock);
-}
-
-// get position (thread-safe)
-v3f64 client_player_get_position()
-{
-       v3f64 pos;
-
-       pthread_rwlock_rdlock(&client_player.rwlock);
-       pos = client_player.pos;
-       pthread_rwlock_unlock(&client_player.rwlock);
-
-       return pos;
-}
-
-// set position (thread-safe)
-void client_player_set_position(v3f64 pos)
-{
-       pthread_rwlock_rdlock(&client_player.rwlock);
-       client_player.pos = pos;
-       pthread_rwlock_unlock(&client_player.rwlock);
+       ClientEntity *entity = client_player_entity();
+       if (!entity)
+               return;
+
+       pthread_rwlock_rdlock(&entity->lock_pos_rot);
+       pthread_rwlock_rdlock(&entity->lock_box_eye);
+
+       if (physics_ground(
+               client_terrain,
+               client_player.movement.collision,
+               entity->data.box,
+               &entity->data.pos,
+               &client_player.velocity
+       ))
+               client_player.velocity.y += client_player.movement.jump;
+
+       pthread_rwlock_unlock(&entity->lock_box_eye);
+       pthread_rwlock_unlock(&entity->lock_pos_rot);
+
+       refcount_drp(&entity->rc);
 }
 
 // to be called every frame
 void client_player_tick(f64 dtime)
 {
-       pthread_rwlock_wrlock(&client_player.rwlock);
-
-       v3f64 old_pos = client_player.pos;
-       v3f64 old_velocity = client_player.velocity;
-
-       if (! client_player.fly)
-               client_player.velocity.y -= 32.0 * dtime;
-
-#define GETS(vec, comp) *(s32 *) ((char *) &vec + offsetof(v3s32, comp))
-#define GETF(vec, comp) *(f64 *) ((char *) &vec + offsetof(v3f64, comp))
-#define PHYSICS(a, b, c) { \
-               f64 v = (GETF(client_player.velocity, a) + GETF(old_velocity, a)) / 2.0f; \
-               if (v == 0.0) \
-                       goto a ## _physics_done; \
-               aabb3s32 box = round_box(get_box()); \
-               v3f64 old_pos = client_player.pos; \
-               GETF(client_player.pos, a) += v * dtime; \
-               if (! client_player.collision) \
-                       goto a ## _physics_done; \
-               s32 dir; \
-               f64 offset; \
-               if (v > 0.0) { \
-                       dir = +1; \
-                       offset = GETF(client_player.box.max, a); \
-                       GETS(box.min, a) = ceil(GETF(old_pos, a) + offset + 0.5); \
-                       GETS(box.max, a) = floor(GETF(client_player.pos, a) + offset + 0.5); \
-               } else { \
-                       dir = -1; \
-                       offset = GETF(client_player.box.min, a); \
-                       GETS(box.min, a) = floor(GETF(old_pos, a) + offset - 0.5); \
-                       GETS(box.max, a) = ceil(GETF(client_player.pos, a) + offset - 0.5); \
-               } \
-               GETS(box.max, a) += dir; \
-               for (s32 a = GETS(box.min, a); a != GETS(box.max, a); a += dir) { \
-                       for (s32 b = GETS(box.min, b); b <= GETS(box.max, b); b++) { \
-                               for (s32 c = GETS(box.min, c); c <= GETS(box.max, c); c++) { \
-                                       if (is_solid(x, y, z)) { \
-                                               GETF(client_player.pos, a) = (f64) a - offset - 0.5 * (f64) dir; \
-                                               GETF(client_player.velocity, a) = 0.0; \
-                                               goto a ## _physics_done; \
-                                       } \
-                               } \
-                       } \
-               } \
-               a ## _physics_done: (void) 0; \
-       }
-
-       PHYSICS(x, y, z)
-       PHYSICS(y, x, z)
-       PHYSICS(z, x, y)
-
-#undef GETS
-#undef GETF
-#undef PHYSICS
-
-       if (! v3f64_equals(old_pos, client_player.pos))
-               update_pos();
-
-       pthread_rwlock_unlock(&client_player.rwlock);
+       ClientEntity *entity = client_player_entity();
+       if (!entity)
+               return;
+
+       pthread_rwlock_rdlock(&client_player.lock_movement);
+       pthread_rwlock_wrlock(&entity->lock_pos_rot);
+       pthread_rwlock_rdlock(&entity->lock_box_eye);
+
+       if (physics_step(
+               client_terrain,
+               client_player.movement.collision,
+               entity->data.box,
+               &entity->data.pos,
+               &client_player.velocity,
+               &(v3f64) {
+                       0.0,
+                       client_player.movement.flight ? 0.0 : -client_player.movement.gravity,
+                       0.0,
+               },
+               dtime
+       ))
+               client_player_update_pos(entity);
+
+       pthread_rwlock_unlock(&entity->lock_box_eye);
+       pthread_rwlock_unlock(&entity->lock_pos_rot);
+       pthread_rwlock_unlock(&client_player.lock_movement);
+
+       refcount_drp(&entity->rc);
 }
index 0de19b9250c267cd39d8b6a0fef29c7f9df390e1..f2ea0e4167f6f11c25f59ae309dcb2d6edc095ab 100644 (file)
@@ -2,29 +2,25 @@
 #define _CLIENT_PLAYER_H_
 
 #include <pthread.h>
-#include "client/client.h"
-#include "client/object.h"
+#include "client/client_entity.h"
 #include "types.h"
 
-extern struct ClientPlayer
-{
-       v3f64 pos;               // feet position
-       v3f64 velocity;          // current velocity
-       aabb3f64 box;            // axis-aligned bounding box (used for collision), with 0, 0, 0 being the feet position
-       f32 yaw, pitch;          // look direction
-       f64 eye_height;          // eye height above feet
-       pthread_rwlock_t rwlock; // used to protect the above properties
-       bool fly;                // can the player fly?
-       bool collision;          // should the player collide with the floor?
-       Object *obj;             // 3D mesh object (currently always invisible), not thread safe
+extern struct ClientPlayer {
+       v3f64 velocity; // velocity is changed and read from the same thread, no lock needed
+       ToClientMovement movement;
+       pthread_rwlock_t lock_movement;
 } client_player;
 
-void client_player_init();                  // ClientPlayer singleton constructor
-void client_player_deinit();                // ClientPlayer singleton destructor
-void client_player_add_to_scene();          // create mesh object
-void client_player_jump();                  // jump if possible
-v3f64 client_player_get_position();         // get position (thread-safe)
-void client_player_set_position(v3f64 pos); // set position (thread-safe)
-void client_player_tick(f64 dtime);         // to be called every frame
+void client_player_init();                           // called on startup
+void client_player_deinit();                         // called on shutdown
 
-#endif
+ClientEntity *client_player_entity();                // grab and return client entity
+
+void client_player_jump();                           // jump if possible
+
+void client_player_update_pos(ClientEntity *entity); // entity needs to be the client entity
+void client_player_update_rot(ClientEntity *entity); // entity needs to be the client entity
+
+void client_player_tick(f64 dtime);                  // to be called every frame
+
+#endif // _CLIENT_PLAYER_H_
diff --git a/src/client/client_terrain.c b/src/client/client_terrain.c
new file mode 100644 (file)
index 0000000..f60dbb1
--- /dev/null
@@ -0,0 +1,304 @@
+#define _GNU_SOURCE // don't worry, GNU extensions are only used when available
+#include <dragonstd/queue.h>
+#include <sched.h>
+#include <stdatomic.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include "client/client.h"
+#include "client/facecache.h"
+#include "client/client_config.h"
+#include "client/client_player.h"
+#include "client/client_terrain.h"
+#include "client/debug_menu.h"
+#include "client/terrain_gfx.h"
+
+#define MAX_REQUESTS 4
+
+Terrain *client_terrain;
+
+static atomic_bool cancel;         // used to notify meshgen and sync thread about quit
+static Queue meshgen_tasks;        // TerrainCHunk * queue (thread safe)
+static pthread_t *meshgen_threads; // consumer threads for meshgen queue
+static pthread_t sync_thread;      // this thread requests new / changed chunks from server
+static u32 load_distance;          // load distance sent by server
+static size_t load_chunks;         // cached number of facecache positions to process every sync step (matches load distance)
+
+// meshgen functions
+
+// dequeue callback to update queue state in a thread safe manner
+static TerrainChunk *set_dequeued(TerrainChunk *chunk)
+{
+       pthread_mutex_lock(&chunk->mtx);
+       ((TerrainChunkMeta *) chunk->extra)->queue = false;
+       pthread_mutex_unlock(&chunk->mtx);
+
+       return chunk;
+}
+
+// mesh generator step
+static void meshgen_step()
+{
+       TerrainChunk *chunk = queue_deq(&meshgen_tasks, (void *) &set_dequeued);
+
+       if (chunk)
+               terrain_gfx_make_chunk_model(chunk);
+}
+
+// sync functions
+
+// send chunk request command to server
+static void request_chunk(v3s32 pos)
+{
+       dragonnet_peer_send_ToServerRequestChunk(client, &(ToServerRequestChunk) {
+               .pos = pos
+       });
+}
+
+// terrain synchronisation step
+static void sync_step()
+{
+       static u64 tick = 0;
+       static v3s32 *old_requests = NULL;
+       static size_t old_num_requests = 0;
+
+       v3f64 player_pos;
+       ClientEntity *entity = client_player_entity();
+
+       if (entity) {
+               pthread_rwlock_rdlock(&entity->lock_pos_rot);
+               player_pos = entity->data.pos;
+               pthread_rwlock_unlock(&entity->lock_pos_rot);
+
+               refcount_drp(&entity->rc);
+       } else {
+               sched_yield();
+               return;
+       }
+
+       v3s32 center = terrain_node_to_chunk_pos((v3s32) {player_pos.x, player_pos.y, player_pos.z}, NULL);
+
+       u64 last_tick = tick++;
+
+       v3s32 *requests = malloc(MAX_REQUESTS * sizeof *requests);
+       size_t num_requests = 0;
+
+       for (size_t i = 0; i < load_chunks; i++) {
+               v3s32 pos = v3s32_add(facecache_get(i), center);
+               TerrainChunk *chunk = terrain_get_chunk(client_terrain, pos, false);
+
+               if (chunk) {
+                       pthread_mutex_lock(&chunk->mtx);
+
+                       TerrainChunkMeta *meta = chunk->extra;
+                       switch (meta->state) {
+                               case CHUNK_READY:
+                                       // re-request chunks that got back into range
+                                       if (meta->sync < last_tick)
+                                               request_chunk(pos);
+                                       __attribute__((fallthrough));
+
+                               case CHUNK_FRESH:
+                                       meta->state = CHUNK_READY;
+                                       meta->sync = tick;
+                                       break;
+
+                               case CHUNK_RECIEVING:
+                                       break;
+                       }
+
+                       pthread_mutex_unlock(&chunk->mtx);
+               } else if (num_requests < MAX_REQUESTS) {
+                       // avoid duplicate requests
+                       bool requested = false;
+
+                       for (size_t i = 0; i < old_num_requests; i++) {
+                               if (v3s32_equals(old_requests[i], pos)) {
+                                       requested = true;
+                                       break;
+                               }
+                       }
+
+                       if (!requested)
+                               request_chunk(pos);
+
+                       requests[num_requests++] = pos;
+               }
+       }
+
+       if (old_requests)
+               free(old_requests);
+
+       old_requests = requests;
+       old_num_requests = num_requests;
+}
+
+// pthread routine for meshgen and sync thread
+
+static struct LoopThread {
+       const char *name;
+       void (*step)();
+} loop_threads[2] = {
+       {"meshgen", &meshgen_step},
+       {   "sync",    &sync_step},
+};
+
+static void *loop_routine(struct LoopThread *thread)
+{
+#ifdef __GLIBC__ // check whether bloat is enabled
+       pthread_setname_np(pthread_self(), thread->name);
+#endif // __GLIBC__
+
+       // warning: extremely advanced logic
+       while (!cancel)
+               thread->step();
+
+       return NULL;
+}
+
+// terrain callbacks
+// note: all these functions require the chunk mutex to be locked, which is always the case when a terrain callback is invoked
+
+// callback for initializing a newly created chunk
+// allocate and initialize meta data
+static void on_create_chunk(TerrainChunk *chunk)
+{
+       TerrainChunkMeta *meta = chunk->extra = malloc(sizeof *meta);
+
+       meta->state = CHUNK_RECIEVING;
+       meta->queue = false;
+       meta->sync = 0;
+       meta->model = NULL;
+       meta->empty = false;
+}
+
+// callback for deleting a chunk
+// free meta data
+static void on_delete_chunk(TerrainChunk *chunk)
+{
+       free(chunk->extra);
+}
+
+// callback for determining whether a chunk should be returned by terrain_get_chunk
+// hold back chunks that have not been fully read from server yet when the create flag is not set
+static bool on_get_chunk(TerrainChunk *chunk, bool create)
+{
+       return create || ((TerrainChunkMeta *) chunk->extra)->state > CHUNK_RECIEVING;
+}
+
+// public functions
+
+// called on startup
+void client_terrain_init()
+{
+       client_terrain = terrain_create();
+       client_terrain->callbacks.create_chunk   = &on_create_chunk;
+       client_terrain->callbacks.delete_chunk   = &on_delete_chunk;
+       client_terrain->callbacks.get_chunk      = &on_get_chunk;
+       client_terrain->callbacks.set_node       = NULL;
+       client_terrain->callbacks.after_set_node = NULL;
+
+       cancel = false;
+       queue_ini(&meshgen_tasks);
+
+       client_terrain_set_load_distance(10); // some initial fuck idk just in case server is stupid
+
+       sync_thread = 0;
+       meshgen_threads = malloc(sizeof *meshgen_threads * client_config.meshgen_threads);
+       for (unsigned int i = 0; i < client_config.meshgen_threads; i++)
+               meshgen_threads[i] = 0; // but why???
+}
+
+// called on shutdown
+void client_terrain_deinit()
+{
+       queue_clr(&meshgen_tasks, NULL, NULL, NULL);
+       terrain_delete(client_terrain);
+}
+
+// start meshgen and sync threads
+void client_terrain_start()
+{
+       for (unsigned int i = 0; i < client_config.meshgen_threads; i++)
+               pthread_create(&meshgen_threads[i], NULL, (void *) &loop_routine, &loop_threads[0]);
+
+       pthread_create(&sync_thread, NULL, (void *) &loop_routine, &loop_threads[1]);
+}
+
+// stop meshgen and sync threads
+void client_terrain_stop()
+{
+       cancel = true;
+       queue_cnl(&meshgen_tasks);
+
+       for (unsigned int i = 0; i < client_config.meshgen_threads; i++)
+               if (meshgen_threads[i])
+                       pthread_join(meshgen_threads[i], NULL);
+       free(meshgen_threads);
+
+       if (sync_thread)
+               pthread_join(sync_thread, NULL);
+}
+
+// update load distance
+void client_terrain_set_load_distance(u32 dist)
+{
+       load_distance = dist;
+       load_chunks = facecache_count(load_distance);
+       debug_menu_changed(ENTRY_LOAD_DISTANCE);
+}
+
+// return load distance
+u32 client_terrain_get_load_distance()
+{
+       return load_distance;
+}
+
+// called when a chunk was recieved from server
+void client_terrain_chunk_received(TerrainChunk *chunk)
+{
+       pthread_mutex_lock(&chunk->mtx);
+       TerrainChunkMeta *extra = chunk->extra;
+       if (extra->state == CHUNK_RECIEVING)
+               extra->state = CHUNK_FRESH;
+       pthread_mutex_unlock(&chunk->mtx);
+
+       client_terrain_meshgen_task(chunk);
+
+       v3s32 neighbors[6] = {
+               {+1,  0,  0},
+               { 0, +1,  0},
+               { 0,  0, +1},
+               {-1,  0,  0},
+               { 0, -1,  0},
+               { 0,  0, -1},
+       };
+
+       for (int i = 0; i < 6; i++)
+               client_terrain_meshgen_task(terrain_get_chunk(client_terrain,
+                       v3s32_add(chunk->pos, neighbors[i]), false));
+}
+
+// enqueue chunk to mesh update queue
+void client_terrain_meshgen_task(TerrainChunk *chunk)
+{
+       if (!chunk)
+               return;
+
+       pthread_mutex_lock(&chunk->mtx);
+
+       TerrainChunkMeta *meta = chunk->extra;
+       if (!meta->queue) {
+               if (meta->empty) {
+                       if (meta->model) {
+                               meta->model->flags.delete = 1;
+                               meta->model = NULL;
+                       }
+               } else {
+                       meta->queue = true;
+                       queue_enq(&meshgen_tasks, chunk);
+               }
+       }
+
+       pthread_mutex_unlock(&chunk->mtx);
+}
diff --git a/src/client/client_terrain.h b/src/client/client_terrain.h
new file mode 100644 (file)
index 0000000..f1e1355
--- /dev/null
@@ -0,0 +1,34 @@
+#ifndef _CLIENT_TERRAIN_H_
+#define _CLIENT_TERRAIN_H_
+
+#include <stdbool.h>
+#include "client/model.h"
+#include "terrain.h"
+#include "types.h"
+
+typedef enum {
+       CHUNK_RECIEVING, // currently deserializing
+       CHUNK_FRESH,     // first deserialisation finished, not processed by sync thread yet
+       CHUNK_READY,     // ready to use and processed by sync thread
+} TerrainChunkState;
+
+typedef struct {
+       TerrainChunkState state; // keep track of the deserialisation and sync processing state
+       bool queue;              // whether the chunk is in meshgen queue
+       u64 sync;                // keep track of when a chunk was synced the last time (used to detect when a chunk got out of and then back into load distance)
+       Model *model;            // generated by terrain_gfx
+       bool empty;              // whether the chunk is all air
+} TerrainChunkMeta;
+
+extern Terrain *client_terrain;
+
+void client_terrain_init();                              // called on startup
+void client_terrain_deinit();                            // called on shutdown
+void client_terrain_set_load_distance(u32 dist);         // update load distance
+u32 client_terrain_get_load_distance();                  // return load distance
+void client_terrain_start();                             // start meshgen and sync threads
+void client_terrain_stop();                              // stop meshgen and sync threads
+void client_terrain_chunk_received(TerrainChunk *chunk); // called when a chunk was recieved from server
+void client_terrain_meshgen_task(TerrainChunk *chunk);   // enqueue chunk to mesh update queue
+
+#endif
index 738407614a8b921265c00a8354a0f40e99418ca3..874d14dd1f84bd2f99db1d247a29a027ab293267 100644 (file)
@@ -1,53 +1,53 @@
 #include "client/cube.h"
 
-Vertex3D cube_vertices[6][6] = {
+CubeVertex cube_vertices[6][6] = {
        {
-               {{-0.5, -0.5, -0.5}, {+0.0, +0.0, -1.0}, +0.0, {+0.0, +0.0}, {+1.0, +1.0, +1.0}},
-               {{+0.5, +0.5, -0.5}, {+0.0, +0.0, -1.0}, +0.0, {+1.0, +1.0}, {+1.0, +1.0, +1.0}},
-               {{+0.5, -0.5, -0.5}, {+0.0, +0.0, -1.0}, +0.0, {+1.0, +0.0}, {+1.0, +1.0, +1.0}},
-               {{+0.5, +0.5, -0.5}, {+0.0, +0.0, -1.0}, +0.0, {+1.0, +1.0}, {+1.0, +1.0, +1.0}},
-               {{-0.5, -0.5, -0.5}, {+0.0, +0.0, -1.0}, +0.0, {+0.0, +0.0}, {+1.0, +1.0, +1.0}},
-               {{-0.5, +0.5, -0.5}, {+0.0, +0.0, -1.0}, +0.0, {+0.0, +1.0}, {+1.0, +1.0, +1.0}},
+               {{-0.5, -0.5, -0.5}, {+0.0, +0.0, -1.0}, {+0.0, +0.0}},
+               {{+0.5, +0.5, -0.5}, {+0.0, +0.0, -1.0}, {+1.0, +1.0}},
+               {{+0.5, -0.5, -0.5}, {+0.0, +0.0, -1.0}, {+1.0, +0.0}},
+               {{+0.5, +0.5, -0.5}, {+0.0, +0.0, -1.0}, {+1.0, +1.0}},
+               {{-0.5, -0.5, -0.5}, {+0.0, +0.0, -1.0}, {+0.0, +0.0}},
+               {{-0.5, +0.5, -0.5}, {+0.0, +0.0, -1.0}, {+0.0, +1.0}},
        },
        {
-               {{-0.5, -0.5, +0.5}, {+0.0, +0.0, +1.0}, +0.0, {+0.0, +0.0}, {+1.0, +1.0, +1.0}},
-               {{+0.5, -0.5, +0.5}, {+0.0, +0.0, +1.0}, +0.0, {+1.0, +0.0}, {+1.0, +1.0, +1.0}},
-               {{+0.5, +0.5, +0.5}, {+0.0, +0.0, +1.0}, +0.0, {+1.0, +1.0}, {+1.0, +1.0, +1.0}},
-               {{+0.5, +0.5, +0.5}, {+0.0, +0.0, +1.0}, +0.0, {+1.0, +1.0}, {+1.0, +1.0, +1.0}},
-               {{-0.5, +0.5, +0.5}, {+0.0, +0.0, +1.0}, +0.0, {+0.0, +1.0}, {+1.0, +1.0, +1.0}},
-               {{-0.5, -0.5, +0.5}, {+0.0, +0.0, +1.0}, +0.0, {+0.0, +0.0}, {+1.0, +1.0, +1.0}},
+               {{-0.5, -0.5, +0.5}, {+0.0, +0.0, +1.0}, {+0.0, +0.0}},
+               {{+0.5, -0.5, +0.5}, {+0.0, +0.0, +1.0}, {+1.0, +0.0}},
+               {{+0.5, +0.5, +0.5}, {+0.0, +0.0, +1.0}, {+1.0, +1.0}},
+               {{+0.5, +0.5, +0.5}, {+0.0, +0.0, +1.0}, {+1.0, +1.0}},
+               {{-0.5, +0.5, +0.5}, {+0.0, +0.0, +1.0}, {+0.0, +1.0}},
+               {{-0.5, -0.5, +0.5}, {+0.0, +0.0, +1.0}, {+0.0, +0.0}},
        },
        {
-               {{-0.5, +0.5, +0.5}, {-1.0, +0.0, +0.0}, +0.0, {+1.0, +1.0}, {+1.0, +1.0, +1.0}},
-               {{-0.5, +0.5, -0.5}, {-1.0, +0.0, +0.0}, +0.0, {+0.0, +1.0}, {+1.0, +1.0, +1.0}},
-               {{-0.5, -0.5, -0.5}, {-1.0, +0.0, +0.0}, +0.0, {+0.0, +0.0}, {+1.0, +1.0, +1.0}},
-               {{-0.5, -0.5, -0.5}, {-1.0, +0.0, +0.0}, +0.0, {+0.0, +0.0}, {+1.0, +1.0, +1.0}},
-               {{-0.5, -0.5, +0.5}, {-1.0, +0.0, +0.0}, +0.0, {+1.0, +0.0}, {+1.0, +1.0, +1.0}},
-               {{-0.5, +0.5, +0.5}, {-1.0, +0.0, +0.0}, +0.0, {+1.0, +1.0}, {+1.0, +1.0, +1.0}},
+               {{-0.5, +0.5, +0.5}, {-1.0, +0.0, +0.0}, {+1.0, +1.0}},
+               {{-0.5, +0.5, -0.5}, {-1.0, +0.0, +0.0}, {+0.0, +1.0}},
+               {{-0.5, -0.5, -0.5}, {-1.0, +0.0, +0.0}, {+0.0, +0.0}},
+               {{-0.5, -0.5, -0.5}, {-1.0, +0.0, +0.0}, {+0.0, +0.0}},
+               {{-0.5, -0.5, +0.5}, {-1.0, +0.0, +0.0}, {+1.0, +0.0}},
+               {{-0.5, +0.5, +0.5}, {-1.0, +0.0, +0.0}, {+1.0, +1.0}},
        },
        {
-               {{+0.5, +0.5, +0.5}, {+1.0, +0.0, +0.0}, +0.0, {+1.0, +1.0}, {+1.0, +1.0, +1.0}},
-               {{+0.5, -0.5, -0.5}, {+1.0, +0.0, +0.0}, +0.0, {+0.0, +0.0}, {+1.0, +1.0, +1.0}},
-               {{+0.5, +0.5, -0.5}, {+1.0, +0.0, +0.0}, +0.0, {+0.0, +1.0}, {+1.0, +1.0, +1.0}},
-               {{+0.5, -0.5, -0.5}, {+1.0, +0.0, +0.0}, +0.0, {+0.0, +0.0}, {+1.0, +1.0, +1.0}},
-               {{+0.5, +0.5, +0.5}, {+1.0, +0.0, +0.0}, +0.0, {+1.0, +1.0}, {+1.0, +1.0, +1.0}},
-               {{+0.5, -0.5, +0.5}, {+1.0, +0.0, +0.0}, +0.0, {+1.0, +0.0}, {+1.0, +1.0, +1.0}},
+               {{+0.5, +0.5, +0.5}, {+1.0, +0.0, +0.0}, {+1.0, +1.0}},
+               {{+0.5, -0.5, -0.5}, {+1.0, +0.0, +0.0}, {+0.0, +0.0}},
+               {{+0.5, +0.5, -0.5}, {+1.0, +0.0, +0.0}, {+0.0, +1.0}},
+               {{+0.5, -0.5, -0.5}, {+1.0, +0.0, +0.0}, {+0.0, +0.0}},
+               {{+0.5, +0.5, +0.5}, {+1.0, +0.0, +0.0}, {+1.0, +1.0}},
+               {{+0.5, -0.5, +0.5}, {+1.0, +0.0, +0.0}, {+1.0, +0.0}},
        },
        {
-               {{-0.5, -0.5, -0.5}, {+0.0, -1.0, +0.0}, +0.0, {+0.0, +1.0}, {+1.0, +1.0, +1.0}},
-               {{+0.5, -0.5, -0.5}, {+0.0, -1.0, +0.0}, +0.0, {+1.0, +1.0}, {+1.0, +1.0, +1.0}},
-               {{+0.5, -0.5, +0.5}, {+0.0, -1.0, +0.0}, +0.0, {+1.0, +0.0}, {+1.0, +1.0, +1.0}},
-               {{+0.5, -0.5, +0.5}, {+0.0, -1.0, +0.0}, +0.0, {+1.0, +0.0}, {+1.0, +1.0, +1.0}},
-               {{-0.5, -0.5, +0.5}, {+0.0, -1.0, +0.0}, +0.0, {+0.0, +0.0}, {+1.0, +1.0, +1.0}},
-               {{-0.5, -0.5, -0.5}, {+0.0, -1.0, +0.0}, +0.0, {+0.0, +1.0}, {+1.0, +1.0, +1.0}},
+               {{-0.5, -0.5, -0.5}, {+0.0, -1.0, +0.0}, {+0.0, +1.0}},
+               {{+0.5, -0.5, -0.5}, {+0.0, -1.0, +0.0}, {+1.0, +1.0}},
+               {{+0.5, -0.5, +0.5}, {+0.0, -1.0, +0.0}, {+1.0, +0.0}},
+               {{+0.5, -0.5, +0.5}, {+0.0, -1.0, +0.0}, {+1.0, +0.0}},
+               {{-0.5, -0.5, +0.5}, {+0.0, -1.0, +0.0}, {+0.0, +0.0}},
+               {{-0.5, -0.5, -0.5}, {+0.0, -1.0, +0.0}, {+0.0, +1.0}},
        },
        {
-               {{-0.5, +0.5, -0.5}, {+0.0, +1.0, +0.0}, +0.0, {+0.0, +1.0}, {+1.0, +1.0, +1.0}},
-               {{+0.5, +0.5, +0.5}, {+0.0, +1.0, +0.0}, +0.0, {+1.0, +0.0}, {+1.0, +1.0, +1.0}},
-               {{+0.5, +0.5, -0.5}, {+0.0, +1.0, +0.0}, +0.0, {+1.0, +1.0}, {+1.0, +1.0, +1.0}},
-               {{+0.5, +0.5, +0.5}, {+0.0, +1.0, +0.0}, +0.0, {+1.0, +0.0}, {+1.0, +1.0, +1.0}},
-               {{-0.5, +0.5, -0.5}, {+0.0, +1.0, +0.0}, +0.0, {+0.0, +1.0}, {+1.0, +1.0, +1.0}},
-               {{-0.5, +0.5, +0.5}, {+0.0, +1.0, +0.0}, +0.0, {+0.0, +0.0}, {+1.0, +1.0, +1.0}},
+               {{-0.5, +0.5, -0.5}, {+0.0, +1.0, +0.0}, {+0.0, +1.0}},
+               {{+0.5, +0.5, +0.5}, {+0.0, +1.0, +0.0}, {+1.0, +0.0}},
+               {{+0.5, +0.5, -0.5}, {+0.0, +1.0, +0.0}, {+1.0, +1.0}},
+               {{+0.5, +0.5, +0.5}, {+0.0, +1.0, +0.0}, {+1.0, +0.0}},
+               {{-0.5, +0.5, -0.5}, {+0.0, +1.0, +0.0}, {+0.0, +1.0}},
+               {{-0.5, +0.5, +0.5}, {+0.0, +1.0, +0.0}, {+0.0, +0.0}},
        },
 };
 
index e467be9b8861a3861b4c81407251f4a5e476ca1e..4fae1fa83b5c78b44d92fc1463e09cba1fbd0551 100644 (file)
@@ -1,8 +1,15 @@
 #ifndef _CUBE_H_
 #define _CUBE_H_
 
-#include "client/object.h"
+#include "client/model.h"
+#include "types.h"
 
-extern Vertex3D cube_vertices[6][6];
+typedef struct {
+       v3f32 position;
+       v3f32 normal;
+       v2f32 textureCoordinates;
+} __attribute__((packed)) CubeVertex;
 
-#endif
+extern CubeVertex cube_vertices[6][6];
+
+#endif // _CUBE_H_
index 04b9256ec62f9669e47fffa61cf36d86b16eaf74..83857078e252bbf122e5e83fca8339f6bfe78b1a 100644 (file)
-#include <stdio.h>
+#include <asprintf/asprintf.h>
 #include <GL/glew.h>
 #include <GL/gl.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <pthread.h>
 #include "client/client_config.h"
-#include "client/client_map.h"
 #include "client/client_player.h"
+#include "client/client_terrain.h"
 #include "client/debug_menu.h"
+#include "client/game.h"
 #include "client/gui.h"
 #include "client/window.h"
 #include "day.h"
 #include "environment.h"
 #include "perlin.h"
-#include "util.h"
 #include "version.h"
 
-typedef enum
-{
-       DME_VERSION,
-       DME_FPS,
-       DME_POS,
-       DME_YAW,
-       DME_PITCH,
-       DME_TIME,
-       DME_DAYLIGHT,
-       DME_SUN_ANGLE,
-       DME_HUMIDITY,
-       DME_TEMPERATURE,
-       DME_SEED,
-       DME_FLIGHT,
-       DME_COLLISION,
-       DME_TIMELAPSE,
-       DME_FULLSCREEN,
-       DME_OPENGL,
-       DME_GPU,
-       DME_ANTIALIASING,
-       DME_MIPMAP,
-       DME_RENDER_DISTANCE,
-       DME_SIMULATION_DISTANCE,
-       DME_COUNT,
-} DebugMenuEntry;
-
-static GUIElement *gui_elements[DME_COUNT] = {NULL};
+static GUIElement *gui_elements[COUNT_ENTRY] = {NULL};
+static bool changed_elements[COUNT_ENTRY] = {false};
+static pthread_mutex_t changed_elements_mtx = PTHREAD_MUTEX_INITIALIZER;
 
 static bool debug_menu_enabled = true;
-static DebugMenuEntry last_always_visible = DME_POS;
+static DebugMenuEntry last_always_visible = ENTRY_POS;
+
+static char *get_entry_text(DebugMenuEntry entry)
+{
+       bool flight = false;
+       bool collision = false;
+       int hours = 0;
+       int minutes = 0;
+       v3f64 pos = {0.0f, 0.0f, 0.0f};
+       v3f32 rot = {0.0f, 0.0f, 0.0f};
+
+       switch (entry) {
+               case ENTRY_POS:
+               case ENTRY_YAW:
+               case ENTRY_PITCH:
+               case ENTRY_HUMIDITY:
+               case ENTRY_TEMPERATURE: {
+                       ClientEntity *entity = client_player_entity();
+                       if (!entity)
+                               return strdup("");
+
+                       pthread_rwlock_rdlock(&entity->lock_pos_rot);
+                       pos = entity->data.pos;
+                       rot = entity->data.rot;
+                       pthread_rwlock_unlock(&entity->lock_pos_rot);
+                       refcount_drp(&entity->rc);
+                       break;
+               }
+
+               case ENTRY_FLIGHT:
+               case ENTRY_COLLISION:
+                       pthread_rwlock_rdlock(&client_player.lock_movement);
+                       flight = client_player.movement.flight;
+                       collision = client_player.movement.collision;
+                       pthread_rwlock_unlock(&client_player.lock_movement);
+                       break;
+
+               case ENTRY_ANTIALIASING:
+                       if (!client_config.antialiasing)
+                               return strdup("antialiasing: disabled");
+                       break;
+
+               case ENTRY_TIME:
+                       split_time_of_day(&hours, &minutes);
+                       break;
+
+               default:
+                       break;
+       }
+
+       char *str;
+       switch (entry) {
+               case ENTRY_VERSION:       asprintf(&str, "Dragonblocks Alpha %s", VERSION                                    ); break;
+               case ENTRY_FPS:           asprintf(&str, "%d FPS", game_fps                                                  ); break;
+               case ENTRY_POS:           asprintf(&str, "(%.1f %.1f %.1f)", pos.x, pos.y, pos.z                             ); break;
+               case ENTRY_YAW:           asprintf(&str, "yaw = %.1f", rot.x / M_PI * 180.0                                  ); break;
+               case ENTRY_PITCH:         asprintf(&str, "pitch = %.1f", rot.y / M_PI * 180.0                                ); break;
+               case ENTRY_TIME:          asprintf(&str, "%02d:%02d", hours, minutes                                         ); break;
+               case ENTRY_DAYLIGHT:      asprintf(&str, "daylight = %.2f", get_daylight()                                   ); break;
+               case ENTRY_SUN_ANGLE:     asprintf(&str, "sun angle = %.1f", fmod(get_sun_angle() / M_PI * 180.0, 360.0)     ); break;
+               case ENTRY_HUMIDITY:      asprintf(&str, "humidity = %.2f", get_humidity((v3s32) {pos.x, pos.y, pos.z})      ); break;
+               case ENTRY_TEMPERATURE:   asprintf(&str, "temperature = %.2f", get_temperature((v3s32) {pos.x, pos.y, pos.z})); break;
+               case ENTRY_SEED:          asprintf(&str, "seed = %d", seed                                                   ); break;
+               case ENTRY_FLIGHT:        asprintf(&str, "flight: %s", flight ? "enabled" : "disabled"                       ); break;
+               case ENTRY_COLLISION:     asprintf(&str, "collision: %s", collision ? "enabled" : "disabled"                 ); break;
+               case ENTRY_TIMELAPSE:     asprintf(&str, "timelapse: %s", timelapse ? "enabled" : "disabled"                 ); break;
+               case ENTRY_FULLSCREEN:    asprintf(&str, "fullscreen: %s", window.fullscreen ? "enabled" : "disabled"        ); break;
+               case ENTRY_OPENGL:        asprintf(&str, "OpenGL %s", glGetString(GL_VERSION)                                ); break;
+               case ENTRY_GPU:           asprintf(&str, "%s", glGetString(GL_RENDERER)                                      ); break;
+               case ENTRY_ANTIALIASING:  asprintf(&str, "antialiasing: %u samples", client_config.antialiasing              ); break;
+               case ENTRY_MIPMAP:        asprintf(&str, "mipmap: %s", client_config.mipmap ? "enabled" : "disabled"         ); break;
+               case ENTRY_VIEW_DISTANCE: asprintf(&str, "view distance: %.1lf", client_config.view_distance                 ); break;
+               case ENTRY_LOAD_DISTANCE: asprintf(&str, "load distance: %u", client_terrain_get_load_distance()             ); break;
+               default: break;
+       }
+       return str;
+}
 
 void debug_menu_init()
 {
        s32 offset = -16;
 
-       for (DebugMenuEntry i = 0; i < DME_COUNT; i++) {
-               gui_elements[i] = gui_add(&gui_root, (GUIElementDefinition) {
+       for (DebugMenuEntry i = 0; i < COUNT_ENTRY; i++) {
+               gui_elements[i] = gui_add(NULL, (GUIElementDefinition) {
                        .pos = {0.0f, 0.0f},
                        .z_index = 0.1f,
                        .offset = {2, offset += 18},
                        .margin = {2, 2},
                        .align = {0.0f, 0.0f},
                        .scale = {1.0f, 1.0f},
-                       .scale_type = GST_TEXT,
+                       .scale_type = SCALE_TEXT,
                        .affect_parent_scale = false,
                        .text = strdup(""),
                        .image = NULL,
@@ -64,124 +119,47 @@ void debug_menu_init()
                        .bg_color = (v4f32) {0.0f, 0.0f, 0.0f, 0.0f},
                });
        }
+
+       debug_menu_toggle();
+
+       debug_menu_changed(ENTRY_VERSION);
+       debug_menu_changed(ENTRY_SEED);
+       debug_menu_changed(ENTRY_TIMELAPSE);
+       debug_menu_changed(ENTRY_FULLSCREEN);
+       debug_menu_changed(ENTRY_OPENGL);
+       debug_menu_changed(ENTRY_GPU);
+       debug_menu_changed(ENTRY_ANTIALIASING);
+       debug_menu_changed(ENTRY_MIPMAP);
+       debug_menu_changed(ENTRY_VIEW_DISTANCE);
 }
 
 void debug_menu_toggle()
 {
-       debug_menu_enabled = ! debug_menu_enabled;
+       debug_menu_enabled = !debug_menu_enabled;
 
-       for (DebugMenuEntry i = 0; i < DME_COUNT; i++) {
+       for (DebugMenuEntry i = 0; i < COUNT_ENTRY; i++) {
                gui_elements[i]->visible = debug_menu_enabled || i <= last_always_visible;
                gui_elements[i]->def.bg_color.w = debug_menu_enabled ? 0.5f : 0.0f;
        }
 }
 
-void debug_menu_update_version()
-{
-       gui_set_text(gui_elements[DME_VERSION], format_string("Dragonblocks Alpha %s", VERSION));
-}
-
-void debug_menu_update_fps(int fps)
-{
-       gui_set_text(gui_elements[DME_FPS], format_string("%d FPS", fps));
-}
-
-void debug_menu_update_pos()
-{
-       gui_set_text(gui_elements[DME_POS], format_string("(%.1f %.1f %.1f)", client_player.pos.x, client_player.pos.y, client_player.pos.z));
-}
-
-void debug_menu_update_yaw()
-{
-       gui_set_text(gui_elements[DME_YAW], format_string("yaw = %.1f", client_player.yaw / M_PI * 180.0));
-}
-
-void debug_menu_update_pitch()
-{
-       gui_set_text(gui_elements[DME_PITCH], format_string("pitch = %.1f", client_player.pitch / M_PI * 180.0));
-}
-
-void debug_menu_update_time()
-{
-       int hours, minutes;
-       split_time_of_day(&hours, &minutes);
-       gui_set_text(gui_elements[DME_TIME], format_string("%02d:%02d", hours, minutes));
-}
-
-void debug_menu_update_daylight()
-{
-       gui_set_text(gui_elements[DME_DAYLIGHT], format_string("daylight = %.2f", get_daylight()));
-}
-
-void debug_menu_update_sun_angle()
-{
-       gui_set_text(gui_elements[DME_SUN_ANGLE], format_string("sun angle = %.1f", fmod(get_sun_angle() / M_PI * 180.0, 360.0)));
-}
-
-void debug_menu_update_humidity()
-{
-       gui_set_text(gui_elements[DME_HUMIDITY], format_string("humidity = %.2f", get_humidity((v3s32) {client_player.pos.x, client_player.pos.y, client_player.pos.z})));
-}
-
-void debug_menu_update_temperature()
-{
-       gui_set_text(gui_elements[DME_TEMPERATURE], format_string("temperature = %.2f", get_temperature((v3s32) {client_player.pos.x, client_player.pos.y, client_player.pos.z})));
-}
-
-void debug_menu_update_seed()
-{
-       gui_set_text(gui_elements[DME_SEED], format_string("seed = %d", seed));
-}
-
-void debug_menu_update_flight()
-{
-       gui_set_text(gui_elements[DME_FLIGHT], format_string("flight: %s", client_player.fly ? "enabled" : "disabled"));
-}
-
-void debug_menu_update_collision()
-{
-       gui_set_text(gui_elements[DME_COLLISION], format_string("collision: %s", client_player.collision ? "enabled" : "disabled"));
-}
-
-void debug_menu_update_timelapse()
+void debug_menu_update()
 {
-       gui_set_text(gui_elements[DME_TIMELAPSE], format_string("timelapse: %s", timelapse ? "enabled" : "disabled"));
-}
-
-void debug_menu_update_fullscreen()
-{
-       gui_set_text(gui_elements[DME_FULLSCREEN], format_string("fullscreen: %s", window.fullscreen ? "enabled" : "disabled"));
-}
-
-void debug_menu_update_opengl()
-{
-       gui_set_text(gui_elements[DME_OPENGL], format_string("OpenGL %s", glGetString(GL_VERSION)));
-}
-
-void debug_menu_update_gpu()
-{
-       gui_set_text(gui_elements[DME_GPU], format_string("%s", glGetString(GL_RENDERER)));
-}
-
-void debug_menu_update_antialiasing()
-{
-       gui_set_text(gui_elements[DME_ANTIALIASING], client_config.antialiasing > 1
-               ? format_string("antialiasing: %u samples", client_config.antialiasing)
-               : format_string("antialiasing: disabled")
-       );
-}
+       bool changed_elements_cpy[COUNT_ENTRY];
 
-void debug_menu_update_mipmap()
-{
-       gui_set_text(gui_elements[DME_MIPMAP], format_string("mipmap: %s", client_config.mipmap ? "enabled" : "disabled"));
-}
+       pthread_mutex_lock(&changed_elements_mtx);
+       memcpy(changed_elements_cpy, changed_elements, COUNT_ENTRY * sizeof(bool));
+       memset(changed_elements, 0,                    COUNT_ENTRY * sizeof(bool));
+       pthread_mutex_unlock(&changed_elements_mtx);
 
-void debug_menu_update_render_distance()
-{
-       gui_set_text(gui_elements[DME_RENDER_DISTANCE], format_string("render distance: %.1lf", client_config.render_distance));
+       for (DebugMenuEntry i = 0; i < COUNT_ENTRY; i++)
+               if (changed_elements_cpy[i])
+                       gui_text(gui_elements[i], get_entry_text(i));
 }
 
-void debug_menu_update_simulation_distance()
+void debug_menu_changed(DebugMenuEntry entry)
 {
-       gui_set_text(gui_elements[DME_SIMULATION_DISTANCE], format_string("simulation distance: %u", client_map.simulation_distance));
+       pthread_mutex_lock(&changed_elements_mtx);
+       changed_elements[entry] = true;
+       pthread_mutex_unlock(&changed_elements_mtx);
 }
index fdba1d1a3c33c0206d7fcc28fd3705f0ed6992c1..3b1b7de58af00d82700ec6659d80e399ec0ee8e1 100644 (file)
@@ -1,31 +1,34 @@
 #ifndef _DEBUG_MENU_H_
 #define _DEBUG_MENU_H_
 
-#include <stdbool.h>
+typedef enum {
+       ENTRY_VERSION,
+       ENTRY_FPS,
+       ENTRY_POS,
+       ENTRY_YAW,
+       ENTRY_PITCH,
+       ENTRY_TIME,
+       ENTRY_DAYLIGHT,
+       ENTRY_SUN_ANGLE,
+       ENTRY_HUMIDITY,
+       ENTRY_TEMPERATURE,
+       ENTRY_SEED,
+       ENTRY_FLIGHT,
+       ENTRY_COLLISION,
+       ENTRY_TIMELAPSE,
+       ENTRY_FULLSCREEN,
+       ENTRY_OPENGL,
+       ENTRY_GPU,
+       ENTRY_ANTIALIASING,
+       ENTRY_MIPMAP,
+       ENTRY_VIEW_DISTANCE,
+       ENTRY_LOAD_DISTANCE,
+       COUNT_ENTRY,
+} DebugMenuEntry;
 
 void debug_menu_init();
 void debug_menu_toggle();
+void debug_menu_changed(DebugMenuEntry entry);
+void debug_menu_update();
 
-void debug_menu_update_version();
-void debug_menu_update_fps(int fps);
-void debug_menu_update_pos();
-void debug_menu_update_yaw();
-void debug_menu_update_pitch();
-void debug_menu_update_time();
-void debug_menu_update_daylight();
-void debug_menu_update_sun_angle();
-void debug_menu_update_humidity();
-void debug_menu_update_temperature();
-void debug_menu_update_seed();
-void debug_menu_update_flight();
-void debug_menu_update_collision();
-void debug_menu_update_timelapse();
-void debug_menu_update_fullscreen();
-void debug_menu_update_opengl();
-void debug_menu_update_gpu();
-void debug_menu_update_antialiasing();
-void debug_menu_update_mipmap();
-void debug_menu_update_render_distance();
-void debug_menu_update_simulation_distance();
-
-#endif
+#endif // _DEBUG_MENU_H_
index f08854d0efe15aa4031b62d43d84da1a0764e7c7..bcb8ba4685f171fa372e73961c850f114f7f4e3e 100644 (file)
@@ -1,31 +1,26 @@
-#include <stdlib.h>
 #include <dragonstd/array.h>
+#include <stdlib.h>
 #include "client/facecache.h"
 
-static struct
-{
-       Array positions;
-       u32 size;
-       pthread_mutex_t mtx;
-} facecache;
+static Array positions;
+static u32 radius;
+static pthread_mutex_t mtx;
 
-__attribute((constructor)) static void face_cache_init()
+__attribute__((constructor)) static void facecache_init()
 {
-       facecache.size = 0;
-       facecache.positions = array_create(sizeof(v3s32));
-       v3s32 pos = {0, 0, 0};
-       array_append(&facecache.positions, &pos);
-       pthread_mutex_init(&facecache.mtx, NULL);
+       array_ini(&positions, sizeof(v3s32), 1000);
+       array_apd(&positions, &(v3s32) {0, 0, 0});
+       pthread_mutex_init(&mtx, NULL);
+       radius = 0;
 }
 
-__attribute((destructor)) void face_cache_deinit()
+__attribute__((destructor)) static void facecache_deinit()
 {
-       if (facecache.positions.ptr)
-               free(facecache.positions.ptr);
-       pthread_mutex_destroy(&facecache.mtx);
+       array_clr(&positions);
+       pthread_mutex_destroy(&mtx);
 }
 
-static void face_cache_calculate(s32 size)
+static inline void facecache_calculate(s32 radius)
 {
 #define ADDPOS(a, b, c, va, vb, vc) \
        { \
@@ -33,13 +28,13 @@ static void face_cache_calculate(s32 size)
                *(s32 *) ((char *) &pos + offsetof(v3s32, a)) = va; \
                *(s32 *) ((char *) &pos + offsetof(v3s32, b)) = vb; \
                *(s32 *) ((char *) &pos + offsetof(v3s32, c)) = vc; \
-               array_append(&facecache.positions, &pos); \
+               array_apd(&positions, &pos); \
        }
 #define SQUARES(a, b, c) \
-       for (s32 va = -size + 1; va < size; va++) { \
-               for (s32 vb = -size + 1; vb < size; vb++) { \
-                       ADDPOS(a, b, c, va, vb,  size) \
-                       ADDPOS(a, b, c, va, vb, -size) \
+       for (s32 va = -radius + 1; va < radius; va++) { \
+               for (s32 vb = -radius + 1; vb < radius; vb++) { \
+                       ADDPOS(a, b, c, va, vb,  radius) \
+                       ADDPOS(a, b, c, va, vb, -radius) \
                } \
        }
        SQUARES(x, z, y)
@@ -47,44 +42,46 @@ static void face_cache_calculate(s32 size)
        SQUARES(z, y, x)
 #undef SQUARES
 #define EDGES(a, b, c) \
-       for (s32 va = -size + 1; va < size; va++) { \
-               ADDPOS(a, b, c, va,  size,  size) \
-               ADDPOS(a, b, c, va,  size, -size) \
-               ADDPOS(a, b, c, va, -size,  size) \
-               ADDPOS(a, b, c, va, -size, -size) \
+       for (s32 va = -radius + 1; va < radius; va++) { \
+               ADDPOS(a, b, c, va,  radius,  radius) \
+               ADDPOS(a, b, c, va,  radius, -radius) \
+               ADDPOS(a, b, c, va, -radius,  radius) \
+               ADDPOS(a, b, c, va, -radius, -radius) \
        }
        EDGES(x, y, z)
        EDGES(z, x, y)
        EDGES(y, x, z)
 #undef EDGES
-       ADDPOS(x, y, z,  size,  size,  size)
-       ADDPOS(x, y, z,  size,  size, -size)
-       ADDPOS(x, y, z,  size, -size,  size)
-       ADDPOS(x, y, z,  size, -size, -size)
-       ADDPOS(x, y, z, -size,  size,  size)
-       ADDPOS(x, y, z, -size,  size, -size)
-       ADDPOS(x, y, z, -size, -size,  size)
-       ADDPOS(x, y, z, -size, -size, -size)
+       ADDPOS(x, y, z,  radius,  radius,  radius)
+       ADDPOS(x, y, z,  radius,  radius, -radius)
+       ADDPOS(x, y, z,  radius, -radius,  radius)
+       ADDPOS(x, y, z,  radius, -radius, -radius)
+       ADDPOS(x, y, z, -radius,  radius,  radius)
+       ADDPOS(x, y, z, -radius,  radius, -radius)
+       ADDPOS(x, y, z, -radius, -radius,  radius)
+       ADDPOS(x, y, z, -radius, -radius, -radius)
 #undef ADDPOS
 }
 
-v3s32 facecache_face(size_t i, v3s32 *base)
+v3s32 facecache_get(size_t i)
 {
-       pthread_mutex_lock(&facecache.mtx);
-       while (facecache.positions.siz <= i)
-               face_cache_calculate(++facecache.size);
-       v3s32 pos = ((v3s32 *) facecache.positions.ptr)[i];
-       pthread_mutex_unlock(&facecache.mtx);
-       if (base) {
-               pos.x += base->x;
-               pos.y += base->y;
-               pos.z += base->z;
+       pthread_mutex_lock(&mtx);
+
+       if (positions.cap <= i) {
+               positions.cap = i;
+               array_rlc(&positions);
        }
+
+       while (positions.siz <= i)
+               facecache_calculate(++radius);
+
+       v3s32 pos = ((v3s32 *) positions.ptr)[i];
+       pthread_mutex_unlock(&mtx);
        return pos;
 }
 
-size_t facecache_count(u32 size)
+size_t facecache_count(u32 radius)
 {
-       size_t len = 1 + size * 2;
+       size_t len = 1 + radius * 2;
        return len * len * len;
 }
index 847b739603f3d8d70edc21886b472e9f2a01c1c9..7b62b9e80f5b5748d1660db656f2f3977d01828a 100644 (file)
@@ -4,7 +4,7 @@
 #include <pthread.h>
 #include "types.h"
 
-v3s32 facecache_face(size_t i, v3s32 *base);
+v3s32 facecache_get(size_t i);
 size_t facecache_count(u32 size);
 
-#endif
+#endif // _FACECACHE_H_
index 97c4177c5c1fc8344a7a0452c9d7986a32d54211..15e432a7ffe99ec925c48689ed5efe629bae64f7 100644 (file)
 
 #define NUM_CHARS 128
 
-typedef struct
-{
-       Texture *texture;
+typedef struct {
+       Texture texture;
        v2s32 bearing;
        u32 advance;
 } Character;
 
-static struct
-{
-       FT_Library library;
-       FT_Face face;
-       Character chars[NUM_CHARS];
-       GLfloat height;
-} font;
-
-typedef struct
-{
-       GLfloat x, y;
-} __attribute__((packed)) VertexFontPosition;
-
-typedef struct
-{
-       GLfloat s, t;
-} __attribute__((packed)) VertexFontTextureCoordinates;
-
-typedef struct
-{
-       VertexFontPosition position;
-       VertexFontTextureCoordinates textureCoordinates;
-} __attribute__((packed)) VertexFont;
-
-static VertexAttribute vertex_attributes[2] = {
-       // position
-       {
-               .type = GL_FLOAT,
-               .length = 2,
-               .size = sizeof(VertexFontPosition),
-       },
-       // textureCoordinates
-       {
-               .type = GL_FLOAT,
-               .length = 2,
-               .size = sizeof(VertexFontTextureCoordinates),
+static FT_Library font_library;
+static FT_Face font_face;
+static Character font_chars[NUM_CHARS];
+static GLfloat font_height;
+
+typedef struct {
+       v2f32 position;
+       v2f32 textureCoordinates;
+} __attribute__((packed)) FontVertex;
+static VertexLayout font_vertex_layout = {
+       .attributes = (VertexAttribute[]) {
+               {GL_FLOAT, 2, sizeof(v2f32)}, // position
+               {GL_FLOAT, 2, sizeof(v2f32)}, // textureCoordinates
        },
-};
-
-static VertexLayout vertex_layout = {
-       .attributes = vertex_attributes,
        .count = 2,
-       .size = sizeof(VertexFont),
+       .size = sizeof(FontVertex),
 };
 
 bool font_init()
 {
-       if (FT_Init_FreeType(&font.library)) {
-               fprintf(stderr, "Failed to initialize Freetype\n");
+       if (FT_Init_FreeType(&font_library)) {
+               fprintf(stderr, "[error] failed to initialize Freetype\n");
                return false;
        }
 
-       if (FT_New_Face(font.library, RESSOURCE_PATH "fonts/Minecraftia.ttf", 0, &font.face)) {
-               fprintf(stderr, "Failed to load Minecraftia.ttf\n");
+       if (FT_New_Face(font_library, RESSOURCE_PATH "fonts/Minecraftia.ttf", 0, &font_face)) {
+               fprintf(stderr, "[error] failed to load Minecraftia.ttf\n");
                return false;
        }
 
        glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
-       FT_Set_Pixel_Sizes(font.face, 0, 16);
+       FT_Set_Pixel_Sizes(font_face, 0, 16);
 
        for (unsigned char c = 0; c < NUM_CHARS; c++) {
-               if (FT_Load_Char(font.face, c, FT_LOAD_RENDER)) {
-                       fprintf(stderr, "Failed to load glyph %c\n", c);
-
-                       font.chars[c] = (Character) {
-                               .texture = NULL,
-                               .bearing = {0, 0},
-                               .advance = 0,
-                       };
-               } else {
-                       font.chars[c] = (Character) {
-                               .texture = texture_create(font.face->glyph->bitmap.buffer, font.face->glyph->bitmap.width, font.face->glyph->bitmap.rows, GL_RED, false),
-                               .bearing = {font.face->glyph->bitmap_left, font.face->glyph->bitmap_top},
-                               .advance = font.face->glyph->advance.x,
-                       };
+               if (FT_Load_Char(font_face, c, FT_LOAD_RENDER)) {
+                       fprintf(stderr, "[warning] failed to load glyph %c\n", c);
+                       font_chars[c].texture.txo = 0;
+                       continue;
                }
+
+               font_chars[c].texture.width = font_face->glyph->bitmap.width;
+               font_chars[c].texture.height = font_face->glyph->bitmap.rows;
+               texture_upload(&font_chars[c].texture, font_face->glyph->bitmap.buffer, GL_RED, false);
+
+               font_chars[c].bearing = (v2s32) {font_face->glyph->bitmap_left, font_face->glyph->bitmap_top};
+               font_chars[c].advance = font_face->glyph->advance.x;
        }
 
-       font.height = font.chars['|'].texture->height;
+       font_height = font_chars['|'].texture.height;
 
-       FT_Done_Face(font.face);
-       FT_Done_FreeType(font.library);
+       FT_Done_Face(font_face);
+       FT_Done_FreeType(font_library);
 
        return true;
 }
 
 void font_deinit()
 {
-       for (unsigned char c = 0; c < NUM_CHARS; c++) {
-               if (font.chars[c].texture)
-                       texture_delete(font.chars[c].texture);
-       }
+       for (unsigned char c = 0; c < NUM_CHARS; c++)
+               texture_destroy(&font_chars[c].texture);
 }
 
 Font *font_create(const char *text)
 {
-       Font *fnt = malloc(sizeof *fnt);
+       Font *font = malloc(sizeof *font);
 
-       size_t len = strlen(text);
-
-       fnt->meshes = malloc(sizeof(Mesh *) * len);
-       fnt->meshes_count = len;
+       font->count = strlen(text);
+       font->meshes = malloc(font->count * sizeof *font->meshes);
+       font->textures = malloc(font->count * sizeof *font->textures);
 
        GLfloat offset = 0.0f;
 
-       for (size_t i = 0; i < len; i++) {
+       for (size_t i = 0; i < font->count; i++) {
                unsigned char c = text[i];
 
-               if (c >= NUM_CHARS || ! font.chars[c].texture)
+               if (c >= NUM_CHARS || !font_chars[c].texture.txo)
                        c = '?';
 
-               Character *ch = &font.chars[c];
+               Character *ch = &font_chars[c];
 
-               GLfloat width = ch->texture->width;
-        GLfloat height = ch->texture->height;
+               GLfloat width = ch->texture.width;
+               GLfloat height = ch->texture.height;
 
                GLfloat x = ch->bearing.x + offset;
-        GLfloat y = font.height - ch->bearing.y;
-
-        VertexFont vertices[6] = {
-            {{x,         y         }, {0.0f, 0.0f}},
-            {{x,         y + height}, {0.0f, 1.0f}},
-            {{x + width, y + height}, {1.0f, 1.0f}},
-            {{x,         y         }, {0.0f, 0.0f}},
-            {{x + width, y + height}, {1.0f, 1.0f}},
-            {{x + width, y         }, {1.0f, 0.0f}},
-        };
-
-               Mesh *mesh = fnt->meshes[i] = mesh_create();
-               mesh->textures = &ch->texture->id;
-               mesh->textures_count = 1;
-               mesh->free_textures = false;
-               mesh->vertices = vertices;
-               mesh->vertices_count = 6;
-               mesh->free_vertices = false;
-               mesh->layout = &vertex_layout;
-               mesh_configure(mesh);
+               GLfloat y = font_height - ch->bearing.y;
+
+               // this is art
+               // selling this as NFT starting price is 10 BTC
+               font->meshes[i].data = (FontVertex[]) {
+                       {{x,         y         }, {0.0f, 0.0f}},
+                       {{x,         y + height}, {0.0f, 1.0f}},
+                       {{x + width, y + height}, {1.0f, 1.0f}},
+                       {{x,         y         }, {0.0f, 0.0f}},
+                       {{x + width, y + height}, {1.0f, 1.0f}},
+                       {{x + width, y         }, {1.0f, 0.0f}},
+               };
+               font->meshes[i].count = 6;
+               font->meshes[i].layout = &font_vertex_layout;
+               font->meshes[i].vao = font->meshes[i].vbo = 0;
+               font->meshes[i].free_data = false;
+               mesh_upload(&font->meshes[i]);
+
+               font->textures[i] = ch->texture.txo;
 
                offset += ch->advance >> 6;
        }
 
-       fnt->size = (v2f32) {offset, font.height};
+       font->size = (v2f32) {offset, font_height};
 
-       return fnt;
+       return font;
 }
 
-void font_delete(Font *fnt)
+void font_delete(Font *font)
 {
-       for (size_t i = 0; i < fnt->meshes_count; i++)
-               mesh_delete(fnt->meshes[i]);
+       for (size_t i = 0; i < font->count; i++)
+               mesh_destroy(&font->meshes[i]);
 
-       free(fnt);
+       free(font->meshes);
+       free(font->textures);
+       free(font);
 }
 
-void font_render(Font *fnt)
+void font_render(Font *font)
 {
-       for (size_t i = 0; i < fnt->meshes_count; i++)
-               mesh_render(fnt->meshes[i]);
+       for (size_t i = 0; i < font->count; i++) {
+               glBindTextureUnit(0, font->textures[i]);
+               mesh_render(&font->meshes[i]);
+       }
 }
index def7410eb08de5e3b42c83b98ebf0e6c8f48acce..b7ba6bd9b5e38f462a68a1d09a8c28b8d56a7664 100644 (file)
@@ -1,22 +1,24 @@
 #ifndef _FONT_H_
 #define _FONT_H_
 
+#include <GL/glew.h>
+#include <GL/gl.h>
 #include <stdbool.h>
 #include <stddef.h>
 #include "client/mesh.h"
 #include "types.h"
 
-typedef struct
-{
+typedef struct {
        v2f32 size;
-       Mesh **meshes;
-       size_t meshes_count;
+       size_t count;
+       Mesh *meshes;
+       GLuint *textures;
 } Font;
 
 bool font_init();
 void font_deinit();
 Font *font_create(const char *text);
-void font_delete(Font *fnt);
-void font_render(Font *fnt);
+void font_delete(Font *font);
+void font_render(Font *font);
 
-#endif
+#endif // _FONT_H_
index e05d6495a6b5dc1c06ba0927ceea2a351435b5f4..f04ade902e7617ffd35be879115dc2f22cee9d05 100644 (file)
@@ -1,7 +1,8 @@
-#include "frustum.h"
+#include "client/camera.h"
+#include "client/frustum.h"
+#include "client/window.h"
 
-typedef enum
-{
+typedef enum {
        PLANE_LEFT,
        PLANE_RIGHT,
        PLANE_BOTTOM,
@@ -11,52 +12,52 @@ typedef enum
        PLANE_COUNT,
 } Plane;
 
-static struct
-{
-       vec3 points[8];
-       vec4 planes[PLANE_COUNT];
-       int cross_indices[PLANE_COUNT][PLANE_COUNT];
-} frustum;
+mat4x4 frustum;
 
-__attribute__((constructor)) static void init_frustum()
+static vec3 points[8];
+static vec4 planes[PLANE_COUNT];
+static int cross_indices[PLANE_COUNT][PLANE_COUNT];
+
+__attribute__((constructor)) static void frustum_init()
 {
        for (Plane a = 0; a < PLANE_COUNT; a++)
                for (Plane b = 0; b < PLANE_COUNT; b++)
-                       frustum.cross_indices[a][b] = a * (9 - a) / 2 + b - 1;
+                       cross_indices[a][b] = a * (9 - a) / 2 + b - 1;
 }
 
-void frustum_update(mat4x4 view_proj)
+void frustum_update()
 {
-       mat4x4 m;
+       mat4x4_mul(frustum, window.projection, camera.view);
 
-       mat4x4_transpose(m, view_proj);
+       mat4x4 m;
+       mat4x4_transpose(m, frustum);
 
-       vec4_add(frustum.planes[PLANE_LEFT], m[3], m[0]);
-       vec4_sub(frustum.planes[PLANE_RIGHT], m[3], m[0]);
-       vec4_add(frustum.planes[PLANE_BOTTOM], m[3], m[1]);
-       vec4_sub(frustum.planes[PLANE_TOP], m[3], m[1]);
-       vec4_add(frustum.planes[PLANE_NEAR], m[3], m[2]);
-       vec4_sub(frustum.planes[PLANE_FAR], m[3], m[2]);
+       vec4_add(planes[PLANE_LEFT], m[3], m[0]);
+       vec4_sub(planes[PLANE_RIGHT], m[3], m[0]);
+       vec4_add(planes[PLANE_BOTTOM], m[3], m[1]);
+       vec4_sub(planes[PLANE_TOP], m[3], m[1]);
+       vec4_add(planes[PLANE_NEAR], m[3], m[2]);
+       vec4_sub(planes[PLANE_FAR], m[3], m[2]);
 
        int i = 0;
        vec3 crosses[PLANE_COUNT * (PLANE_COUNT - 1) / 2];
        for (Plane a = 0; a < PLANE_COUNT; a++)
                for (Plane b = a + 1; b < PLANE_COUNT; b++)
-                       vec3_mul_cross(crosses[i++], frustum.planes[a], frustum.planes[b]);
+                       vec3_mul_cross(crosses[i++], planes[a], planes[b]);
 
        int j = 0;
        for (Plane c = PLANE_NEAR; c <= PLANE_FAR; c++) {
                for (Plane a = PLANE_LEFT; a <= PLANE_RIGHT; a++) {
                        for (Plane b = PLANE_BOTTOM; b <= PLANE_TOP; b++) {
-                               float d = -1.0f / vec3_mul_inner(frustum.planes[a], crosses[frustum.cross_indices[b][c]]);
-                               vec3 w = {frustum.planes[a][3], frustum.planes[b][3], frustum.planes[c][3]};
-                               float *res = frustum.points[j++];
+                               float d = -1.0f / vec3_mul_inner(planes[a], crosses[cross_indices[b][c]]);
+                               vec3 w = {planes[a][3], planes[b][3], planes[c][3]};
+                               float *res = points[j++];
 
-                               vec3 res_1_cross = {-crosses[frustum.cross_indices[a][c]][0], -crosses[frustum.cross_indices[a][c]][1], -crosses[frustum.cross_indices[a][c]][2]};
+                               vec3 res_1_cross = {-crosses[cross_indices[a][c]][0], -crosses[cross_indices[a][c]][1], -crosses[cross_indices[a][c]][2]};
 
-                               res[0] = vec3_mul_inner(crosses[frustum.cross_indices[b][c]], w) * d;
+                               res[0] = vec3_mul_inner(crosses[cross_indices[b][c]], w) * d;
                                res[1] = vec3_mul_inner(res_1_cross, w) * d;
-                               res[2] = vec3_mul_inner(crosses[frustum.cross_indices[a][b]], w) * d;
+                               res[2] = vec3_mul_inner(crosses[cross_indices[a][b]], w) * d;
                        }
                }
        }
@@ -74,7 +75,7 @@ static bool outside_plane(Plane i, aabb3f32 box)
                                        1.0f,
                                };
 
-                               if (vec4_mul_inner(frustum.planes[i], plane) > 0.0)
+                               if (vec4_mul_inner(planes[i], plane) > 0.0)
                                        return false;
                        }
                }
@@ -84,7 +85,7 @@ static bool outside_plane(Plane i, aabb3f32 box)
 }
 
 // http://iquilezles.org/www/articles/frustumcorrect/frustumcorrect.htm
-bool frustum_is_visible(aabb3f32 box)
+static bool box_visible(aabb3f32 box)
 {
        for (Plane i = 0; i < PLANE_COUNT; i++) {
                if (outside_plane(i, box))
@@ -95,12 +96,12 @@ bool frustum_is_visible(aabb3f32 box)
 
        for (Plane i = 0; i < PLANE_COUNT; i++) {
                int outside[6] = {
-                       frustum.points[i][0] > box.max.x,
-                       frustum.points[i][0] < box.min.x,
-                       frustum.points[i][1] > box.max.y,
-                       frustum.points[i][1] < box.min.y,
-                       frustum.points[i][2] > box.max.z,
-                       frustum.points[i][2] < box.min.z,
+                       points[i][0] > box.max.x,
+                       points[i][0] < box.min.x,
+                       points[i][1] > box.max.y,
+                       points[i][1] < box.min.y,
+                       points[i][2] > box.max.z,
+                       points[i][2] < box.min.z,
                };
 
                for (int i = 0; i < 6; i++)
@@ -114,3 +115,8 @@ bool frustum_is_visible(aabb3f32 box)
 
        return true;
 }
+
+bool frustum_cull(aabb3f32 box)
+{
+       return !box_visible(box);
+}
index fa8eba59b89e8b054eb6f162fd0f093a1837cdaf..a7472659f0bdb394d3c4ab1745c6297ad1069a9e 100644 (file)
@@ -5,7 +5,9 @@
 #include <linmath.h/linmath.h>
 #include "types.h"
 
-void frustum_update(mat4x4 view_proj);
-bool frustum_is_visible(aabb3f32 box);
+extern mat4x4 frustum;
+
+void frustum_update();
+bool frustum_cull(aabb3f32 box);
 
 #endif
index 02216ac0b40a93a2493b8635946ed67fcaf04e52..99da5785bf41cf6d6d6ab87dfe3af74479315e02 100644 (file)
@@ -1,42 +1,42 @@
-#include <stdio.h>
-#include <unistd.h>
+#define STB_IMAGE_WRITE_IMPLEMENTATION
 #include <GL/glew.h>
 #include <GL/gl.h>
 #include <GLFW/glfw3.h>
-#define STB_IMAGE_WRITE_IMPLEMENTATION
 #include <stb/stb_image_write.h>
+#include <stdio.h>
+#include <unistd.h>
 #include "client/camera.h"
 #include "client/client.h"
-#include "client/client_map.h"
 #include "client/client_node.h"
 #include "client/client_player.h"
+#include "client/client_terrain.h"
 #include "client/debug_menu.h"
 #include "client/font.h"
+#include "client/frustum.h"
 #include "client/gui.h"
 #include "client/input.h"
-#include "client/scene.h"
 #include "client/sky.h"
 #include "client/window.h"
 #include "day.h"
 #include "interrupt.h"
 
-int window_width, window_height;
+int game_fps = 0;
 
 static void crosshair_init()
 {
-       gui_add(&gui_root, (GUIElementDefinition) {
+       gui_add(NULL, (GUIElementDefinition) {
                .pos = {0.5f, 0.5f},
                .z_index = 0.0f,
                .offset = {0, 0},
                .margin = {0, 0},
                .align = {0.5f, 0.5f},
                .scale = {1.0f, 1.0f},
-               .scale_type = GST_IMAGE,
+               .scale_type = SCALE_IMAGE,
                .affect_parent_scale = false,
                .text = NULL,
                .image = texture_load(RESSOURCE_PATH "textures/crosshair.png", false),
-               .text_color = (v4f32) {0.0f, 0.0f, 0.0f, 0.0f},
-               .bg_color = (v4f32) {0.0f, 0.0f, 0.0f, 0.0f},
+               .text_color = {0.0f, 0.0f, 0.0f, 0.0f},
+               .bg_color = {0.0f, 0.0f, 0.0f, 0.0f},
        });
 }
 
@@ -52,28 +52,33 @@ static void render(f64 dtime)
        glAlphaFunc(GL_GREATER, 0.1f);
        glCullFace(GL_BACK);
        glFrontFace(GL_CCW);
+
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
 
+       frustum_update();
+       terrain_gfx_update();
+
        sky_render();
-       scene_render(dtime);
+       model_scene_render(dtime);
        gui_render();
 }
 
 static void game_loop()
 {
        f64 fps_update_timer = 1.0f;
-       int frames = 0;
+       unsigned int frames = 0;
 
        struct timespec ts, ts_old;
        clock_gettime(CLOCK_REALTIME, &ts_old);
 
-       while (! glfwWindowShouldClose(window.handle) && ! interrupt->done) {
+       while (!glfwWindowShouldClose(window.handle) && !interrupt.set) {
                clock_gettime(CLOCK_REALTIME, &ts);
                f64 dtime = (f64) (ts.tv_sec - ts_old.tv_sec) + (f64) (ts.tv_nsec - ts_old.tv_nsec) / 1.0e9;
                ts_old = ts;
 
                if ((fps_update_timer -= dtime) <= 0.0) {
-                       debug_menu_update_fps(frames);
+                       debug_menu_changed(ENTRY_FPS);
+                       game_fps = frames;
                        fps_update_timer += 1.0;
                        frames = 0;
                }
@@ -83,9 +88,10 @@ static void game_loop()
                input_tick(dtime);
                client_player_tick(dtime);
 
-               debug_menu_update_time();
-               debug_menu_update_daylight();
-               debug_menu_update_sun_angle();
+               debug_menu_changed(ENTRY_TIME);
+               debug_menu_changed(ENTRY_DAYLIGHT);
+               debug_menu_changed(ENTRY_SUN_ANGLE);
+               debug_menu_update();
 
                render(dtime);
 
@@ -94,116 +100,95 @@ static void game_loop()
        }
 }
 
-bool game()
+bool game(Flag *gfx_init)
 {
-       window_width = 1250;
-       window_height = 750;
-
-       if (! window_init(window_width, window_height))
+       if (!window_init())
                return false;
 
-       if (! font_init())
+       if (!font_init())
                return false;
 
-       if (! scene_init())
-               return false;
+       model_init();
 
-       scene_on_resize(window_width, window_height);
+       if (!sky_init())
+               return false;
 
-       if (! sky_init())
+       if (!terrain_gfx_init())
                return false;
 
        client_node_init();
-       client_map_start();
+       client_terrain_start();
 
        camera_set_position((v3f32) {0.0f, 0.0f, 0.0f});
        camera_set_angle(0.0f, 0.0f);
 
-       if (! gui_init())
+       if (!gui_init())
                return false;
 
-       gui_on_resize(window_width, window_height);
-
        debug_menu_init();
-       debug_menu_toggle();
-       debug_menu_update_fps(0);
-       debug_menu_update_version();
-       debug_menu_update_seed();
-       debug_menu_update_flight();
-       debug_menu_update_collision();
-       debug_menu_update_timelapse();
-       debug_menu_update_fullscreen();
-       debug_menu_update_opengl();
-       debug_menu_update_gpu();
-       debug_menu_update_antialiasing();
-       debug_menu_update_mipmap();
-       debug_menu_update_render_distance();
-       debug_menu_update_simulation_distance();
-
        crosshair_init();
-
        input_init();
 
-       client_player_add_to_scene();
-
+       flag_set(gfx_init);
        game_loop();
 
-       client_map_stop();
+       client_terrain_stop();
 
        font_deinit();
        gui_deinit();
-       scene_deinit();
+       model_deinit();
        sky_deinit();
+       terrain_gfx_deinit();
 
        return true;
 }
 
-char *take_screenshot()
+char *game_take_screenshot()
 {
        // renderbuffer for depth & stencil buffer
-       GLuint RBO;
-       glGenRenderbuffers(1, &RBO);
-       glBindRenderbuffer(GL_RENDERBUFFER, RBO);
-       glRenderbufferStorageMultisample(GL_RENDERBUFFER, 8, GL_DEPTH24_STENCIL8, window_width, window_height);
+       GLuint rbo;
+       glGenRenderbuffers(1, &rbo);
+       glBindRenderbuffer(GL_RENDERBUFFER, rbo);
+       glRenderbufferStorageMultisample(GL_RENDERBUFFER, 8, GL_DEPTH24_STENCIL8, window.width, window.height);
 
        // 2 textures, one with AA, one without
 
-       GLuint textures[2];
-       glGenTextures(2, textures);
+       GLuint txos[2];
+       glGenTextures(2, txos);
 
-       glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, textures[0]);
-       glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 8, GL_RGB, window_width, window_height, GL_TRUE);
+       glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, txos[0]);
+       glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 8, GL_RGB, window.width, window.height, GL_TRUE);
 
-       glBindTexture(GL_TEXTURE_2D, textures[1]);
-       glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, window_width, window_height, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
+       glBindTexture(GL_TEXTURE_2D, txos[1]);
+       glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, window.width, window.height, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
 
        // 2 framebuffers, one with AA, one without
 
-       GLuint FBOs[2];
-       glGenFramebuffers(2, FBOs);
+       GLuint fbos[2];
+       glGenFramebuffers(2, fbos);
 
-       glBindFramebuffer(GL_FRAMEBUFFER, FBOs[0]);
-       glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, textures[0], 0);
-       glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, RBO);
+       glBindFramebuffer(GL_FRAMEBUFFER, fbos[0]);
+       glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, txos[0], 0);
+       glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rbo);
 
-       glBindFramebuffer(GL_FRAMEBUFFER, FBOs[1]);
-       glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textures[1], 0);
+       glBindFramebuffer(GL_FRAMEBUFFER, fbos[1]);
+       glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, txos[1], 0);
 
        // render scene
-       glBindFramebuffer(GL_FRAMEBUFFER, FBOs[0]);
+       glBindFramebuffer(GL_FRAMEBUFFER, fbos[0]);
        render(0.0);
        glBindFramebuffer(GL_FRAMEBUFFER, 0);
 
        // blit AA-buffer into no-AA buffer
-       glBindFramebuffer(GL_READ_FRAMEBUFFER, FBOs[0]);
-       glBindFramebuffer(GL_DRAW_FRAMEBUFFER, FBOs[1]);
-       glBlitFramebuffer(0, 0, window_width, window_height, 0, 0, window_width, window_height, GL_COLOR_BUFFER_BIT, GL_NEAREST);
+       glBindFramebuffer(GL_READ_FRAMEBUFFER, fbos[0]);
+       glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbos[1]);
+       glBlitFramebuffer(0, 0, window.width, window.height, 0, 0, window.width, window.height, GL_COLOR_BUFFER_BIT, GL_NEAREST);
 
        // read data
-       GLubyte data[window_width * window_height * 3];
-       glBindFramebuffer(GL_FRAMEBUFFER, FBOs[1]);
+       GLubyte data[window.width * window.height * 3];
+       glBindFramebuffer(GL_FRAMEBUFFER, fbos[1]);
        glPixelStorei(GL_PACK_ALIGNMENT, 1);
-       glReadPixels(0, 0, window_width, window_height, GL_RGB, GL_UNSIGNED_BYTE, data);
+       glReadPixels(0, 0, window.width, window.height, GL_RGB, GL_UNSIGNED_BYTE, data);
 
        // create filename
        char filename[BUFSIZ];
@@ -212,18 +197,12 @@ char *take_screenshot()
 
        // save screenshot
        stbi_flip_vertically_on_write(true);
-       stbi_write_png(filename, window_width, window_height, 3, data, window_width * 3);
+       stbi_write_png(filename, window.width, window.height, 3, data, window.width * 3);
 
        // delete buffers
-       glDeleteRenderbuffers(1, &RBO);
-       glDeleteTextures(2, textures);
-       glDeleteFramebuffers(2, FBOs);
+       glDeleteRenderbuffers(1, &rbo);
+       glDeleteTextures(2, txos);
+       glDeleteFramebuffers(2, fbos);
 
        return strdup(filename);
 }
-
-void game_on_resize(int width, int height)
-{
-       window_width = width;
-       window_height = height;
-}
index b6c04bd7d61418fe13dcf933e18f70fbbb3087bb..af1d74ca143c1ead29b6b9d240d6a4d0f434bc39 100644 (file)
@@ -1,8 +1,11 @@
 #ifndef _GAME_H_
 #define _GAME_H_
 
-bool game();
-char *take_screenshot();
-void game_on_resize(int width, int height);
+#include <dragonstd/flag.h>
 
-#endif
+extern int game_fps;
+
+bool game(Flag *gfx_init);
+char *game_take_screenshot();
+
+#endif // _GAME_H_
index 11fa50a4f585699e3bcbb033163ed0f962fc606e..29f6b916e413ff00e5fa79dd65d2ca6aaee3029b 100644 (file)
-#include <assert.h>
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
 #include <GL/glew.h>
 #include <GL/gl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
 #include "client/client.h"
 #include "client/cube.h"
 #include "client/gui.h"
 #include "client/mesh.h"
 #include "client/shader.h"
-#include "client/vertex.h"
-#include "util.h"
+#include "client/window.h"
+
+static GUIElement root_element;
+
+static GLuint background_prog;
+static GLint background_loc_model;
+static GLint background_loc_projection;
+static GLint background_loc_color;
+typedef struct {
+       v2f32 position;
+} __attribute__((packed)) BackgroundVertex;
+static Mesh background_mesh = {
+       .layout = &(VertexLayout) {
+               .attributes = (VertexAttribute[]) {
+                       {GL_FLOAT, 2, sizeof(v2f32)}, // position
+               },
+               .count = 1,
+               .size = sizeof(BackgroundVertex),
+       },
+       .vao = 0,
+       .vbo = 0,
+       .data = (BackgroundVertex[]) {
+               {{0.0, 0.0}},
+               {{1.0, 0.0}},
+               {{1.0, 1.0}},
+               {{1.0, 1.0}},
+               {{0.0, 1.0}},
+               {{0.0, 0.0}},
+       },
+       .count = 6,
+       .free_data = false,
+};
 
-static struct
-{
-       List elements;
+static GLuint image_prog;
+static GLint image_loc_model;
+static GLint image_loc_projection;
+typedef struct {
+       v2f32 position;
+       v2f32 textureCoordinates;
+} __attribute__((packed)) ImageVertex;
+static Mesh image_mesh = {
+       .layout = &(VertexLayout) {
+               .attributes = (VertexAttribute[]) {
+                       {GL_FLOAT, 2, sizeof(v2f32)}, // position
+                       {GL_FLOAT, 2, sizeof(v2f32)}, // textureCoordinates
+               },
+               .count = 2,
+               .size = sizeof(ImageVertex),
+       },
+       .vao = 0,
+       .vbo = 0,
+       .data = (ImageVertex[]) {
+               {{0.0, 0.0}, {0.0, 0.0}},
+               {{1.0, 0.0}, {1.0, 0.0}},
+               {{1.0, 1.0}, {1.0, 1.0}},
+               {{1.0, 1.0}, {1.0, 1.0}},
+               {{0.0, 1.0}, {0.0, 1.0}},
+               {{0.0, 0.0}, {0.0, 0.0}},
+       },
+       .count = 6,
+       .free_data = false,
+};
 
-       GLuint background_prog;
-       GLint background_loc_model;
-       GLint background_loc_projection;
-       GLint background_loc_color;
-       Mesh *background_mesh;
+static GLuint font_prog;
+static GLint font_loc_model;
+static GLint font_loc_projection;
+static GLint font_loc_color;
+// font meshes are initialized in font.c
 
-       GLuint image_prog;
-       GLint image_loc_model;
-       GLint image_loc_projection;
-       Mesh *image_mesh;
+static mat4x4 projection;
 
-       GLuint font_prog;
-       GLint font_loc_model;
-       GLint font_loc_projection;
-       GLint font_loc_color;
+// element functions
 
-       mat4x4 projection;
-} gui;
+static void delete_element(GUIElement *element);
+static void render_element(GUIElement *element);
+static void scale_element(GUIElement *element);
 
-GUIElement gui_root;
+static int cmp_element(const GUIElement *ea, const GUIElement *eb)
+{
+       return f32_cmp(&ea->def.z_index, &eb->def.z_index);
+}
 
-typedef struct
+static void delete_elements(Array *elements)
 {
-       GLfloat x, y;
-} __attribute__((packed)) VertexBackgroundPosition;
+       for (size_t i = 0; i < elements->siz; i++)
+               delete_element(((GUIElement **) elements->ptr)[i]);
+       array_clr(elements);
+}
 
-typedef struct
+static void delete_element(GUIElement *element)
 {
-       VertexBackgroundPosition position;
-} __attribute__((packed)) VertexBackground;
-
-static VertexAttribute background_vertex_attributes[1] = {
-       // position
-       {
-               .type = GL_FLOAT,
-               .length = 2,
-               .size = sizeof(VertexBackgroundPosition),
-       },
-};
+       delete_elements(&element->children);
 
-static VertexLayout background_vertex_layout = {
-       .attributes = background_vertex_attributes,
-       .count = 1,
-       .size = sizeof(VertexBackground),
-};
+       if (element->def.text)
+               free(element->def.text);
 
-static VertexBackground background_vertices[6] = {
-       {{0.0, 0.0}},
-       {{1.0, 0.0}},
-       {{1.0, 1.0}},
-       {{1.0, 1.0}},
-       {{0.0, 1.0}},
-       {{0.0, 0.0}},
-};
+       if (element->text)
+               font_delete(element->text);
 
-typedef struct
+       free(element);
+}
+
+static void render_elements(Array *elements)
 {
-       GLfloat x, y;
-} __attribute__((packed)) VertexImagePosition;
+       for (size_t i = 0; i < elements->siz; i++)
+               render_element(((GUIElement **) elements->ptr)[i]);
+}
 
-typedef struct
+static void render_element(GUIElement *element)
 {
-       GLfloat s, t;
-} __attribute__((packed)) VertexImageTextureCoordinates;
+       if (element->visible) {
+               if (element->def.bg_color.w > 0.0f) {
+                       glUseProgram(background_prog);
+                       glUniformMatrix4fv(background_loc_model, 1, GL_FALSE, element->transform[0]);
+                       glUniform4f(background_loc_color, element->def.bg_color.x, element->def.bg_color.y, element->def.bg_color.z, element->def.bg_color.w);
+                       mesh_render(&background_mesh);
+               }
+
+               if (element->def.image) {
+                       glUseProgram(image_prog);
+                       glUniformMatrix4fv(image_loc_model, 1, GL_FALSE, element->transform[0]);
+                       glBindTextureUnit(0, element->def.image->txo);
+                       mesh_render(&image_mesh);
+               }
 
-typedef struct
+               if (element->text && element->def.text_color.w > 0.0f) {
+                       glUseProgram(font_prog);
+                       glUniformMatrix4fv(font_loc_model, 1, GL_FALSE, element->text_transform[0]);
+                       glUniform4f(font_loc_color, element->def.text_color.x, element->def.text_color.y, element->def.text_color.z, element->def.text_color.w);
+                       font_render(element->text);
+               }
+
+               render_elements(&element->children);
+       }
+}
+
+static void scale_elements(Array *elements, int mask, v3f32 *max)
 {
-       VertexImagePosition position;
-       VertexImageTextureCoordinates textureCoordinates;
-} __attribute__((packed)) VertexImage;
-
-static VertexAttribute image_vertex_attributes[2] = {
-       // position
-       {
-               .type = GL_FLOAT,
-               .length = 2,
-               .size = sizeof(VertexImagePosition),
-       },
-       // textureCoordinates
-       {
-               .type = GL_FLOAT,
-               .length = 2,
-               .size = sizeof(VertexImageTextureCoordinates),
-       },
-};
+       for (size_t i = 0; i < elements->siz; i++) {
+               GUIElement *element = ((GUIElement **) elements->ptr)[i];
 
-static VertexLayout image_vertex_layout = {
-       .attributes = image_vertex_attributes,
-       .count = 2,
-       .size = sizeof(VertexImage),
-};
+               if ((1 << element->def.affect_parent_scale) & mask) {
+                       scale_element(element);
 
-static VertexImage image_vertices[6] = {
-       {{0.0, 0.0}, {0.0, 0.0}},
-       {{1.0, 0.0}, {1.0, 0.0}},
-       {{1.0, 1.0}, {1.0, 1.0}},
-       {{1.0, 1.0}, {1.0, 1.0}},
-       {{0.0, 1.0}, {0.0, 1.0}},
-       {{0.0, 0.0}, {0.0, 0.0}},
-};
+                       if (max) {
+                               if (element->scale.x > max->x)
+                                       max->x = element->scale.x;
 
-static int bintree_compare_f32(void *v1, void *v2, unused Bintree *tree)
+                               if (element->scale.y > max->y)
+                                       max->y = element->scale.y;
+                       }
+               }
+       }
+}
+
+static void scale_element(GUIElement *element)
+{
+       element->scale = (v2f32) {
+               element->def.scale.x,
+               element->def.scale.y,
+       };
+
+       switch (element->def.scale_type) {
+               case SCALE_IMAGE:
+                       element->scale.x *= element->def.image->width;
+                       element->scale.y *= element->def.image->height;
+                       break;
+
+               case SCALE_TEXT:
+                       element->scale.x *= element->text->size.x;
+                       element->scale.y *= element->text->size.y;
+                       break;
+
+               case SCALE_PARENT:
+                       element->scale.x *= element->parent->scale.x;
+                       element->scale.y *= element->parent->scale.y;
+                       break;
+
+               case SCALE_CHILDREN: {
+                       v3f32 scale = {0.0f, 0.0f, 0.0f};
+                       scale_elements(&element->children, 1 << true, &scale);
+
+                       element->scale.x *= scale.x;
+                       element->scale.y *= scale.y;
+
+                       scale_elements(&element->children, 1 << false, NULL);
+                       break;
+               }
+
+               case SCALE_NONE:
+                       break;
+       }
+
+       if (element->def.scale_type != SCALE_CHILDREN)
+               scale_elements(&element->children, (1 << true) | (1 << false), NULL);
+}
+
+static void transform_element(GUIElement *element)
 {
-       f32 diff = (*(f32 *) v1) - (*(f32 *) v2);
-       return CMPBOUNDS(diff);
+       element->pos = (v2f32) {
+               floor(element->parent->pos.x + element->def.offset.x + element->def.pos.x * element->parent->scale.x - element->def.align.x * element->scale.x),
+               floor(element->parent->pos.y + element->def.offset.y + element->def.pos.y * element->parent->scale.y - element->def.align.y * element->scale.y),
+       };
+
+       mat4x4_translate(element->transform, element->pos.x - element->def.margin.x, element->pos.y - element->def.margin.y, 0.0f);
+       mat4x4_translate(element->text_transform, element->pos.x, element->pos.y, 0.0f);
+       mat4x4_scale_aniso(element->transform, element->transform, element->scale.x + element->def.margin.x * 2.0f, element->scale.y + element->def.margin.y * 2.0f, 1.0f);
+
+       for (size_t i = 0; i < element->children.siz; i++)
+               transform_element(((GUIElement **) element->children.ptr)[i]);
 }
 
+// public functions
+
 bool gui_init()
 {
        // initialize background pipeline
 
-       if (! shader_program_create(RESSOURCE_PATH "shaders/gui/background", &gui.background_prog, NULL)) {
-               fprintf(stderr, "Failed to create GUI background shader program\n");
+       if (!shader_program_create(RESSOURCE_PATH "shaders/gui/background", &background_prog, NULL)) {
+               fprintf(stderr, "[error] failed to create GUI background shader program\n");
                return false;
        }
 
-       gui.background_loc_model = glGetUniformLocation(gui.background_prog, "model");
-       gui.background_loc_projection = glGetUniformLocation(gui.background_prog, "projection");
-       gui.background_loc_color = glGetUniformLocation(gui.background_prog, "color");
-
-       gui.background_mesh = mesh_create();
-       gui.background_mesh->textures = NULL;
-       gui.background_mesh->textures_count = 0;
-       gui.background_mesh->free_textures = false;
-       gui.background_mesh->vertices = background_vertices;
-       gui.background_mesh->vertices_count = 6;
-       gui.background_mesh->free_vertices = false;
-       gui.background_mesh->layout = &background_vertex_layout;
+       background_loc_model = glGetUniformLocation(background_prog, "model");
+       background_loc_projection = glGetUniformLocation(background_prog, "projection");
+       background_loc_color = glGetUniformLocation(background_prog, "color");
 
        // initialize image pipeline
 
-       if (! shader_program_create(RESSOURCE_PATH "shaders/gui/image", &gui.image_prog, NULL)) {
-               fprintf(stderr, "Failed to create GUI image shader program\n");
+       if (!shader_program_create(RESSOURCE_PATH "shaders/gui/image", &image_prog, NULL)) {
+               fprintf(stderr, "[error] failed to create GUI image shader program\n");
                return false;
        }
 
-       gui.image_loc_model = glGetUniformLocation(gui.image_prog, "model");
-       gui.image_loc_projection = glGetUniformLocation(gui.image_prog, "projection");
-
-       gui.image_mesh = mesh_create();
-       gui.image_mesh->textures = NULL;
-       gui.image_mesh->textures_count = 1;
-       gui.image_mesh->free_textures = false;
-       gui.image_mesh->vertices = image_vertices;
-       gui.image_mesh->vertices_count = 6;
-       gui.image_mesh->free_vertices = false;
-       gui.image_mesh->layout = &image_vertex_layout;
+       image_loc_model = glGetUniformLocation(image_prog, "model");
+       image_loc_projection = glGetUniformLocation(image_prog, "projection");
 
        // initialize font pipeline
 
-       if (! shader_program_create(RESSOURCE_PATH "shaders/gui/font", &gui.font_prog, NULL)) {
-               fprintf(stderr, "Failed to create GUI font shader program\n");
+       if (!shader_program_create(RESSOURCE_PATH "shaders/gui/font", &font_prog, NULL)) {
+               fprintf(stderr, "[error] failed to create GUI font shader program\n");
                return false;
        }
 
-       gui.font_loc_model = glGetUniformLocation(gui.font_prog, "model");
-       gui.font_loc_projection = glGetUniformLocation(gui.font_prog, "projection");
-       gui.font_loc_color = glGetUniformLocation(gui.font_prog, "color");
-
-       // font meshes are initialized in font.c
+       font_loc_model = glGetUniformLocation(font_prog, "model");
+       font_loc_projection = glGetUniformLocation(font_prog, "projection");
+       font_loc_color = glGetUniformLocation(font_prog, "color");
 
        // initialize GUI root element
 
-       gui_root.def.pos = (v2f32) {0.0f, 0.0f};
-       gui_root.def.z_index = 0.0f;
-       gui_root.def.offset = (v2s32) {0, 0};
-       gui_root.def.align = (v2f32) {0.0f, 0.0f};
-       gui_root.def.scale = (v2f32) {0.0f, 0.0f};
-       gui_root.def.scale_type = GST_NONE;
-       gui_root.def.affect_parent_scale = false;
-       gui_root.def.text = NULL;
-       gui_root.def.image = NULL;
-       gui_root.def.text_color = (v4f32) {0.0f, 0.0f, 0.0f, 0.0f};
-       gui_root.def.bg_color = (v4f32) {0.0f, 0.0f, 0.0f, 0.0f};
-       gui_root.visible = true;
-       gui_root.pos = (v2f32)  {0.0f, 0.0f};
-       gui_root.scale = (v2f32) {0.0f, 0.0f};
-       gui_root.text = NULL;
-       gui_root.parent = &gui_root;
-       gui_root.children = bintree_create(sizeof(f32), &bintree_compare_f32);
+       root_element.def.pos = (v2f32) {0.0f, 0.0f};
+       root_element.def.z_index = 0.0f;
+       root_element.def.offset = (v2s32) {0, 0};
+       root_element.def.align = (v2f32) {0.0f, 0.0f};
+       root_element.def.scale = (v2f32) {0.0f, 0.0f};
+       root_element.def.scale_type = SCALE_NONE;
+       root_element.def.affect_parent_scale = false;
+       root_element.def.text = NULL;
+       root_element.def.image = NULL;
+       root_element.def.text_color = (v4f32) {0.0f, 0.0f, 0.0f, 0.0f};
+       root_element.def.bg_color = (v4f32) {0.0f, 0.0f, 0.0f, 0.0f};
+       root_element.visible = true;
+       root_element.pos = (v2f32)  {0.0f, 0.0f};
+       root_element.scale = (v2f32) {0.0f, 0.0f};
+       root_element.text = NULL;
+       root_element.parent = &root_element;
+       array_ini(&root_element.children, sizeof(GUIElement *), 0);
+
+       gui_update_projection();
 
        return true;
 }
 
-static void free_element(BintreeNode *node, unused void *arg)
-{
-       GUIElement *element = node->value;
-
-       bintree_clear(&element->children, &free_element, NULL);
-
-       if (element->def.text)
-               free(element->def.text);
-
-       if (element->text)
-               font_delete(element->text);
-
-       free(element);
-}
-
 void gui_deinit()
 {
-       glDeleteProgram(gui.background_prog);
-       mesh_delete(gui.background_mesh);
+       glDeleteProgram(background_prog);
+       mesh_destroy(&background_mesh);
 
-       glDeleteProgram(gui.image_prog);
-       mesh_delete(gui.image_mesh);
+       glDeleteProgram(image_prog);
+       mesh_destroy(&image_mesh);
 
-       glDeleteProgram(gui.font_prog);
+       glDeleteProgram(font_prog);
 
-       bintree_clear(&gui_root.children, &free_element, NULL);
+       delete_elements(&root_element.children);
 }
 
-void gui_on_resize(int width, int height)
+void gui_update_projection()
 {
-       mat4x4_ortho(gui.projection, 0, width, height, 0, -1.0f, 1.0f);
-       glProgramUniformMatrix4fv(gui.background_prog, gui.background_loc_projection, 1, GL_FALSE, gui.projection[0]);
-       glProgramUniformMatrix4fv(gui.image_prog, gui.image_loc_projection, 1, GL_FALSE, gui.projection[0]);
-       glProgramUniformMatrix4fv(gui.font_prog, gui.font_loc_projection, 1, GL_FALSE, gui.projection[0]);
+       mat4x4_ortho(projection, 0, window.width, window.height, 0, -1.0f, 1.0f);
+       glProgramUniformMatrix4fv(background_prog, background_loc_projection, 1, GL_FALSE, projection[0]);
+       glProgramUniformMatrix4fv(image_prog, image_loc_projection, 1, GL_FALSE, projection[0]);
+       glProgramUniformMatrix4fv(font_prog, font_loc_projection, 1, GL_FALSE, projection[0]);
 
-       gui_root.def.scale.x = width;
-       gui_root.def.scale.y = height;
+       root_element.def.scale.x = window.width;
+       root_element.def.scale.y = window.height;
 
-       gui_update_transform(&gui_root);
-}
-
-static void render_element(BintreeNode *node, unused void *arg)
-{
-       GUIElement *element = node->value;
-
-       if (element->visible) {
-               if (element->def.bg_color.w > 0.0f) {
-                       glUseProgram(gui.background_prog);
-                       glUniformMatrix4fv(gui.background_loc_model, 1, GL_FALSE, element->transform[0]);
-                       glUniform4f(gui.background_loc_color, element->def.bg_color.x, element->def.bg_color.y, element->def.bg_color.z, element->def.bg_color.w);
-                       mesh_render(gui.background_mesh);
-               }
-
-               if (element->def.image) {
-                       glUseProgram(gui.image_prog);
-                       glUniformMatrix4fv(gui.image_loc_model, 1, GL_FALSE, element->transform[0]);
-                       gui.image_mesh->textures = &element->def.image->id;
-                       mesh_render(gui.image_mesh);
-               }
-
-               if (element->text && element->def.text_color.w > 0.0f) {
-                       glUseProgram(gui.font_prog);
-                       glUniformMatrix4fv(gui.font_loc_model, 1, GL_FALSE, element->text_transform[0]);
-                       glUniform4f(gui.font_loc_color, element->def.text_color.x, element->def.text_color.y, element->def.text_color.z, element->def.text_color.w);
-                       font_render(element->text);
-               }
-
-               bintree_traverse(&element->children, BTT_INORDER, &render_element, NULL);
-       }
+       gui_transform(&root_element);
 }
 
 void gui_render()
 {
        glDisable(GL_CULL_FACE);
        glDisable(GL_DEPTH_TEST);
-       bintree_traverse(&gui_root.children, BTT_INORDER, &render_element, NULL);
+
+       render_elements(&root_element.children);
+
        glEnable(GL_DEPTH_TEST);
        glEnable(GL_CULL_FACE);
 }
 
 GUIElement *gui_add(GUIElement *parent, GUIElementDefinition def)
 {
-       GUIElement *element = malloc(sizeof(GUIElement));
+       if (parent == NULL)
+               parent = &root_element;
+
+       GUIElement *element = malloc(sizeof *element);
        element->def = def;
        element->visible = true;
        element->parent = parent;
@@ -294,19 +335,18 @@ GUIElement *gui_add(GUIElement *parent, GUIElementDefinition def)
                element->text = NULL;
        }
 
-       bintree_insert(&parent->children, &element->def.z_index, element);
-
-       element->children = bintree_create(sizeof(f32), &bintree_compare_f32);
+       array_ins(&parent->children, &element, (void *) &cmp_element);
+       array_ini(&element->children, sizeof(GUIElement), 0);
 
        if (element->def.affect_parent_scale)
-               gui_update_transform(parent);
+               gui_transform(parent);
        else
-               gui_update_transform(element);
+               gui_transform(element);
 
        return element;
 }
 
-void gui_set_text(GUIElement *element, char *text)
+void gui_text(GUIElement *element, char *text)
 {
        if (element->def.text)
                free(element->def.text);
@@ -314,120 +354,11 @@ void gui_set_text(GUIElement *element, char *text)
        element->def.text = text;
        font_delete(element->text);
        element->text = font_create(text);
-       gui_update_transform(element);
-}
-
-// transform code
-
-typedef struct
-{
-       List left_nodes;
-       v2f32 result;
-} PrecalculateChildrenScaleData;
-
-static void precalculate_children_scale(BintreeNode *node, void *arg);
-static void bintree_calculate_element_scale(BintreeNode *node, void *arg);
-static void list_calculate_element_scale(void *key, void *value, void *arg);
-static void bintree_calculate_element_transform(BintreeNode *node, unused void *arg);
-
-static void calculate_element_scale(GUIElement *element)
-{
-       element->scale = (v2f32) {
-               element->def.scale.x,
-               element->def.scale.y,
-       };
-
-       bool traversed_children = false;
-
-       switch (element->def.scale_type) {
-               case GST_IMAGE:
-                       element->scale.x *= element->def.image->width;
-                       element->scale.y *= element->def.image->height;
-                       break;
-
-               case GST_TEXT:
-                       element->scale.x *= element->text->size.x;
-                       element->scale.y *= element->text->size.y;
-                       break;
-
-               case GST_PARENT:
-                       element->scale.x *= element->parent->scale.x;
-                       element->scale.y *= element->parent->scale.y;
-                       break;
-
-               case GST_CHILDREN: {
-                       PrecalculateChildrenScaleData pdata = {
-                               .left_nodes = list_create(NULL),
-                               .result = {0.0f, 0.0f},
-                       };
-
-                       bintree_traverse(&element->children, BTT_INORDER, &precalculate_children_scale, &pdata);
-
-                       element->scale.x *= pdata.result.x;
-                       element->scale.y *= pdata.result.y;
-
-                       list_clear_func(&pdata.left_nodes, &list_calculate_element_scale, NULL);
-                       traversed_children = true;
-               } break;
-
-               case GST_NONE:
-                       break;
-       }
-
-       if (! traversed_children)
-               bintree_traverse(&element->children, BTT_INORDER, &bintree_calculate_element_scale, NULL);
-}
-
-static void precalculate_children_scale(BintreeNode *node, void *arg)
-{
-       GUIElement *element = node->value;
-       PrecalculateChildrenScaleData *pdata = arg;
-
-       if (element->def.affect_parent_scale) {
-               assert(element->def.scale_type != GST_PARENT);
-               calculate_element_scale(element);
-
-               if (element->scale.x > pdata->result.x)
-                       pdata->result.x = element->scale.x;
-
-               if (element->scale.y > pdata->result.y)
-                       pdata->result.y = element->scale.y;
-       } else {
-               list_put(&pdata->left_nodes, element, NULL);
-       }
-}
-
-static void bintree_calculate_element_scale(BintreeNode *node, unused void *arg)
-{
-       calculate_element_scale(node->value);
-}
-
-static void list_calculate_element_scale(void *key, unused void *value, unused void *arg)
-{
-       calculate_element_scale(key);
-}
-
-static void calculate_element_transform(GUIElement *element)
-{
-       element->pos = (v2f32) {
-               floor(element->parent->pos.x + element->def.offset.x + element->def.pos.x * element->parent->scale.x - element->def.align.x * element->scale.x),
-               floor(element->parent->pos.y + element->def.offset.y + element->def.pos.y * element->parent->scale.y - element->def.align.y * element->scale.y),
-       };
-
-       mat4x4_translate(element->transform, element->pos.x - element->def.margin.x, element->pos.y - element->def.margin.y, 0.0f);
-       mat4x4_translate(element->text_transform, element->pos.x, element->pos.y, 0.0f);
-       mat4x4_scale_aniso(element->transform, element->transform, element->scale.x + element->def.margin.x * 2.0f, element->scale.y + element->def.margin.y * 2.0f, 1.0f);
-
-       bintree_traverse(&element->children, BTT_INORDER, &bintree_calculate_element_transform, NULL);
-}
-
-static void bintree_calculate_element_transform(BintreeNode *node, unused void *arg)
-{
-       calculate_element_transform(node->value);
+       gui_transform(element);
 }
 
-void gui_update_transform(GUIElement *element)
+void gui_transform(GUIElement *element)
 {
-       calculate_element_scale(element);
-       calculate_element_transform(element);
+       scale_element(element);
+       transform_element(element);
 }
index 7fd1844820405dba9814f366cffcd5a3de541ed0..7f4c0d2479690ef11ee3fa9e88b188caff7f2639 100644 (file)
@@ -1,25 +1,22 @@
 #ifndef _GUI_H_
 #define _GUI_H_
 
-#include <stdbool.h>
+#include <dragonstd/array.h>
 #include <linmath.h/linmath.h>
-#include <dragonstd/bintree.h>
-#include <dragonstd/list.h>
+#include <stdbool.h>
 #include "client/font.h"
 #include "client/texture.h"
 #include "types.h"
 
-typedef enum
-{
-       GST_IMAGE,
-       GST_TEXT,
-       GST_PARENT,
-       GST_CHILDREN,
-       GST_NONE,
+typedef enum {
+       SCALE_IMAGE,
+       SCALE_TEXT,
+       SCALE_PARENT,
+       SCALE_CHILDREN,
+       SCALE_NONE,
 } GUIScaleType;
 
-typedef struct
-{
+typedef struct {
        v2f32 pos;
        f32 z_index;
        v2s32 offset;
@@ -34,8 +31,7 @@ typedef struct
        v4f32 bg_color;
 } GUIElementDefinition;
 
-typedef struct GUIElement
-{
+typedef struct GUIElement {
        GUIElementDefinition def;
        bool visible;
        v2f32 pos;
@@ -44,17 +40,15 @@ typedef struct GUIElement
        mat4x4 text_transform;
        Font *text;
        struct GUIElement *parent;
-       Bintree children;
+       Array children;
 } GUIElement;
 
 bool gui_init();
 void gui_deinit();
-void gui_on_resize(int width, int height);
+void gui_update_projection();
 void gui_render();
 GUIElement *gui_add(GUIElement *parent, GUIElementDefinition def);
-void gui_set_text(GUIElement *element, char *text);
-void gui_update_transform(GUIElement *element);
-
-extern GUIElement gui_root;
+void gui_text(GUIElement *element, char *text);
+void gui_transform(GUIElement *element);
 
-#endif
+#endif // _GUI_H_
index a862017d801b33c2aab1550c3e9701319006ffde..e9b154c32808fb39b10b5bd75353e82f4e4365ea 100644 (file)
@@ -1,3 +1,4 @@
+#include <asprintf/asprintf.h>
 #include <math.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include "client/input.h"
 #include "client/window.h"
 #include "day.h"
-#include "util.h"
 
-typedef struct
-{
+typedef struct {
        int key;
-       bool was_pressed;
-       bool fired;
+       bool state;
 } KeyListener;
 
-static struct
-{
-       GUIElement *pause_menu;
-       GUIElement *status_message;
-       bool paused;
-       KeyListener pause_listener;
-       KeyListener fullscreen_listener;
-       KeyListener fly_listener;
-       KeyListener collision_listener;
-       KeyListener timelapse_listener;
-       KeyListener debug_menu_listener;
-       KeyListener screenshot_listener;
-} input;
-
-void input_on_cursor_pos(double current_x, double current_y)
-{
-       if (input.paused)
-               return;
-
-       static double last_x, last_y = 0.0;
+static bool paused = false;
 
-       double delta_x = current_x - last_x;
-       double delta_y = current_y - last_y;
-       last_x = current_x;
-       last_y = current_y;
+static GUIElement *pause_menu;
+static GUIElement *status_message;
 
-       client_player.yaw += (f32) delta_x * M_PI / 180.0f / 8.0f;
-       client_player.pitch -= (f32) delta_y * M_PI / 180.0f / 8.0f;
+static KeyListener listener_pause      = {GLFW_KEY_ESCAPE, false};
+static KeyListener listener_fullscreen = {GLFW_KEY_F11,    false};
+static KeyListener listener_fly        = {GLFW_KEY_F,      false};
+static KeyListener listener_collision  = {GLFW_KEY_C,      false};
+static KeyListener listener_timelapse  = {GLFW_KEY_T,      false};
+static KeyListener listener_debug_menu = {GLFW_KEY_F3,     false};
+static KeyListener listener_screenshot = {GLFW_KEY_F2,     false};
 
-       client_player.yaw = fmod(client_player.yaw + M_PI * 2.0f, M_PI * 2.0f);
-       client_player.pitch = f32_clamp(client_player.pitch, -M_PI / 2.0f + 0.01f, M_PI / 2.0f - 0.01f);
-
-       camera_set_angle(client_player.yaw, client_player.pitch);
-
-       debug_menu_update_yaw();
-       debug_menu_update_pitch();
-}
+static double cursor_last_x = 0.0;
+static double cursor_last_y = 0.0;
 
+// movement mutex needs to be locked
 static bool move(int forward, int backward, vec3 dir)
 {
-       f64 sign;
-       f64 speed = client_player.fly ? 25.0f : 4.317f;
+       // 25.0f; 4.317f
+       f32 sign;
 
        if (glfwGetKey(window.handle, forward) == GLFW_PRESS)
                sign = +1.0f;
@@ -69,9 +46,9 @@ static bool move(int forward, int backward, vec3 dir)
        else
                return false;
 
-       client_player.velocity.x += dir[0] * speed * sign;
-       client_player.velocity.y += dir[1] * speed * sign;
-       client_player.velocity.z += dir[2] * speed * sign;
+       client_player.velocity.x += dir[0] * client_player.movement.speed * sign;
+       client_player.velocity.y += dir[1] * client_player.movement.speed * sign;
+       client_player.velocity.z += dir[2] * client_player.movement.speed * sign;
 
        return true;
 }
@@ -79,156 +56,174 @@ static bool move(int forward, int backward, vec3 dir)
 static void enter_game()
 {
        glfwSetInputMode(window.handle, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
-       input.pause_menu->visible = false;
+       pause_menu->visible = false;
 }
 
-static void do_key_listener(KeyListener *listener)
+static bool key_listener(KeyListener *listener)
 {
-       bool is_pressed = glfwGetKey(window.handle, listener->key) == GLFW_PRESS;
-       listener->fired = listener->was_pressed && ! is_pressed;
-       listener->was_pressed = is_pressed;
+       bool was = listener->state;
+       return !(listener->state = (glfwGetKey(window.handle, listener->key) == GLFW_PRESS)) && was;
 }
 
-static KeyListener create_key_listener(int key)
+static void set_status_message(char *message)
 {
-       return (KeyListener) {
-               .key = key,
-               .was_pressed = false,
-               .fired = false,
-       };
+       gui_text(status_message, message);
+       status_message->def.text_color.w = 1.01f;
 }
 
-static void set_status_message(char *message)
+void input_init()
 {
-       gui_set_text(input.status_message, message);
-       input.status_message->def.text_color.w = 1.01f;
+       pause_menu = gui_add(NULL, (GUIElementDefinition) {
+               .pos = {0.0f, 0.0f},
+               .z_index = 0.5f,
+               .offset = {0, 0},
+               .margin = {0, 0},
+               .align = {0.0f, 0.0f},
+               .scale = {1.0f, 1.0f},
+               .scale_type = SCALE_PARENT,
+               .affect_parent_scale = false,
+               .text = NULL,
+               .image = NULL,
+               .text_color = {0.0f, 0.0f, 0.0f, 0.0f},
+               .bg_color = {0.0f, 0.0f, 0.0f, 0.4f},
+       });
+
+       status_message = gui_add(NULL, (GUIElementDefinition) {
+               .pos = {0.5f, 0.25f},
+               .z_index = 0.1f,
+               .offset = {0, 0},
+               .margin = {0, 0},
+               .align = {0.5f, 0.5f},
+               .scale = {1.0f, 1.0f},
+               .scale_type = SCALE_TEXT,
+               .affect_parent_scale = false,
+               .text = strdup(""),
+               .image = NULL,
+               .text_color = {1.0f, 0.91f, 0.13f, 0.0f},
+               .bg_color = {0.0f, 0.0f, 0.0f, 0.0f},
+       });
+
+       glfwSetInputMode(window.handle, GLFW_STICKY_KEYS, GL_TRUE);
+       enter_game();
 }
 
 void input_tick(f64 dtime)
 {
-       if (input.status_message->def.text_color.w > 1.0f)
-               input.status_message->def.text_color.w = 1.0f;
-       else if (input.status_message->def.text_color.w > 0.0f)
-               input.status_message->def.text_color.w -= dtime * 1.0f;
+       if (status_message->def.text_color.w > 1.0f)
+               status_message->def.text_color.w = 1.0f;
+       else if (status_message->def.text_color.w > 0.0f)
+               status_message->def.text_color.w -= dtime * 1.0f;
 
-       do_key_listener(&input.pause_listener);
-       do_key_listener(&input.fullscreen_listener);
+       if (key_listener(&listener_pause)) {
+               paused = !paused;
 
-       if (input.pause_listener.fired) {
-               input.paused = ! input.paused;
-
-               if (input.paused) {
+               if (paused) {
                        glfwSetInputMode(window.handle, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
-                       input.pause_menu->visible = true;
+                       pause_menu->visible = true;
                } else {
                        enter_game();
                }
        }
 
-       if (input.fullscreen_listener.fired) {
+       if (key_listener(&listener_fullscreen)) {
                if (window.fullscreen)
                        window_exit_fullscreen();
                else
                        window_enter_fullscreen();
        }
 
-       if (! input.paused) {
-               do_key_listener(&input.fly_listener);
-               do_key_listener(&input.collision_listener);
-               do_key_listener(&input.timelapse_listener);
-               do_key_listener(&input.debug_menu_listener);
-               do_key_listener(&input.screenshot_listener);
-
-               if (input.fly_listener.fired) {
-                       client_player.fly = ! client_player.fly;
-                       debug_menu_update_flight();
-                       set_status_message(format_string("Flight %s", client_player.fly ? "Enabled" : "Disabled"));
+       if (!paused) {
+               if (key_listener(&listener_fly)) {
+                       pthread_rwlock_wrlock(&client_player.lock_movement);
+                       client_player.movement.flight = !client_player.movement.flight;
+
+                       char *msg;
+                       asprintf(&msg, "Flight %s", client_player.movement.flight ? "Enabled" : "Disabled");
+                       set_status_message(msg);
+                       debug_menu_changed(ENTRY_FLIGHT);
+
+                       pthread_rwlock_unlock(&client_player.lock_movement);
                }
 
-               if (input.collision_listener.fired) {
-                       client_player.collision = ! client_player.collision;
-                       debug_menu_update_collision();
-                       set_status_message(format_string("Collision %s", client_player.collision ? "Enabled" : "Disabled"));
+               if (key_listener(&listener_collision)) {
+                       pthread_rwlock_wrlock(&client_player.lock_movement);
+                       client_player.movement.collision = !client_player.movement.collision;
+
+                       char *msg;
+                       asprintf(&msg, "Collision %s", client_player.movement.collision ? "Enabled" : "Disabled");
+                       set_status_message(msg);
+                       debug_menu_changed(ENTRY_COLLISION);
+
+                       pthread_rwlock_unlock(&client_player.lock_movement);
                }
 
-               if (input.timelapse_listener.fired) {
+               if (key_listener(&listener_timelapse)) {
                        f64 current_time = get_time_of_day();
-                       timelapse = ! timelapse;
+                       timelapse = !timelapse;
                        set_time_of_day(current_time);
-                       debug_menu_update_timelapse();
-                       set_status_message(format_string("Timelapse %s", timelapse ? "Enabled" : "Disabled"));
+
+                       char *msg;
+                       asprintf(&msg, "Timelapse %s", timelapse ? "Enabled" : "Disabled");
+                       set_status_message(msg);
+                       debug_menu_changed(ENTRY_TIMELAPSE);
                }
 
-               if (input.debug_menu_listener.fired)
+               if (key_listener(&listener_debug_menu))
                        debug_menu_toggle();
 
-               if (input.screenshot_listener.fired) {
-                       char *screenshot_filename = take_screenshot();
-                       set_status_message(format_string("Screenshot saved to %s", screenshot_filename));
+               if (key_listener(&listener_screenshot)) {
+                       char *screenshot_filename = game_take_screenshot();
+                       char *msg;
+                       asprintf(&msg, "Screenshot saved to %s", screenshot_filename);
+                       set_status_message(msg);
                        free(screenshot_filename);
                }
        }
 
+       pthread_rwlock_rdlock(&client_player.lock_movement);
+
        client_player.velocity.x = 0.0f;
        client_player.velocity.z = 0.0f;
 
-       if (client_player.fly)
+       if (client_player.movement.flight)
                client_player.velocity.y = 0.0f;
 
-       if (! input.paused) {
+       if (!paused) {
                move(GLFW_KEY_W, GLFW_KEY_S, camera.movement_dirs.front);
                move(GLFW_KEY_D, GLFW_KEY_A, camera.movement_dirs.right);
 
-               if (client_player.fly)
+               if (client_player.movement.flight)
                        move(GLFW_KEY_SPACE, GLFW_KEY_LEFT_SHIFT, camera.movement_dirs.up);
                else if (glfwGetKey(window.handle, GLFW_KEY_SPACE) == GLFW_PRESS)
                        client_player_jump();
        }
+
+       pthread_rwlock_unlock(&client_player.lock_movement);
 }
 
-void input_init()
+void input_cursor(double current_x, double current_y)
 {
-       input.paused = false;
+       if (paused)
+               return;
 
-       input.pause_listener = create_key_listener(GLFW_KEY_ESCAPE);
-       input.fullscreen_listener = create_key_listener(GLFW_KEY_F11);
-       input.fly_listener = create_key_listener(GLFW_KEY_F);
-       input.collision_listener = create_key_listener(GLFW_KEY_C);
-       input.timelapse_listener = create_key_listener(GLFW_KEY_T);
-       input.debug_menu_listener = create_key_listener(GLFW_KEY_F3);
-       input.screenshot_listener = create_key_listener(GLFW_KEY_F2);
+       double delta_x = current_x - cursor_last_x;
+       double delta_y = current_y - cursor_last_y;
+       cursor_last_x = current_x;
+       cursor_last_y = current_y;
 
-       input.pause_menu = gui_add(&gui_root, (GUIElementDefinition) {
-               .pos = {0.0f, 0.0f},
-               .z_index = 0.5f,
-               .offset = {0, 0},
-               .margin = {0, 0},
-               .align = {0.0f, 0.0f},
-               .scale = {1.0f, 1.0f},
-               .scale_type = GST_PARENT,
-               .affect_parent_scale = false,
-               .text = NULL,
-               .image = NULL,
-               .text_color = {0.0f, 0.0f, 0.0f, 0.0f},
-               .bg_color = {0.0f, 0.0f, 0.0f, 0.4f},
-       });
+       ClientEntity *entity = client_player_entity();
+       if (!entity)
+               return;
 
-       input.status_message = gui_add(&gui_root, (GUIElementDefinition) {
-               .pos = {0.5f, 0.25f},
-               .z_index = 0.1f,
-               .offset = {0, 0},
-               .margin = {0, 0},
-               .align = {0.5f, 0.5f},
-               .scale = {1.0f, 1.0f},
-               .scale_type = GST_TEXT,
-               .affect_parent_scale = false,
-               .text = strdup(""),
-               .image = NULL,
-               .text_color = {1.0f, 0.91f, 0.13f, 0.0f},
-               .bg_color = {0.0f, 0.0f, 0.0f, 0.0f},
-       });
+       pthread_rwlock_wrlock(&entity->lock_pos_rot);
 
-       glfwSetInputMode(window.handle, GLFW_STICKY_KEYS, GL_TRUE);
+       entity->data.rot.x += (f32) delta_x * M_PI / 180.0f / 8.0f;
+       entity->data.rot.y -= (f32) delta_y * M_PI / 180.0f / 8.0f;
 
-       enter_game();
+       entity->data.rot.x = fmod(entity->data.rot.x + M_PI * 2.0f, M_PI * 2.0f);
+       entity->data.rot.y = f32_clamp(entity->data.rot.y, -M_PI / 2.0f + 0.01f, M_PI / 2.0f - 0.01f);
+
+       client_player_update_rot(entity);
+       pthread_rwlock_unlock(&entity->lock_pos_rot);
+       refcount_drp(&entity->rc);
 }
index fd27cf61da787d91b6a789208793a60147d4eb84..9d63151ca567c832a1b77aed9032066ebffd8881 100644 (file)
@@ -3,8 +3,8 @@
 
 #include "types.h"
 
-void input_tick(f64 dtime);
 void input_init();
-void input_on_cursor_pos(double current_x, double current_y);
+void input_tick(f64 dtime);
+void input_cursor(double current_x, double current_y);
 
-#endif
+#endif // _INPUT_H_
index 5d42f8c868e7a72729fec1078d3404575febb268..f2ded84a8199fa34db8c2312820738e53fbb65f9 100644 (file)
@@ -1,63 +1,59 @@
-#include <stdlib.h>
 #include <stddef.h>
+#include <stdlib.h>
 #include "client/mesh.h"
 
-Mesh *mesh_create()
-{
-       Mesh *mesh = malloc(sizeof(Mesh));
-       mesh->VAO = mesh->VBO = 0;
-       mesh->free_textures = false;
-       mesh->free_vertices = false;
-
-       return mesh;
-}
-
-void mesh_delete(Mesh *mesh)
+// upload data to GPU (only done once)
+void mesh_upload(Mesh *mesh)
 {
-       if (mesh->textures && mesh->free_textures)
-               free(mesh->textures);
-
-       if (mesh->vertices && mesh->free_vertices)
-               free(mesh->vertices);
-
-       if (mesh->VAO)
-               glDeleteVertexArrays(1, &mesh->VAO);
+       glGenVertexArrays(1, &mesh->vao);
+       glGenBuffers(1, &mesh->vbo);
 
-       if (mesh->VBO)
-               glDeleteBuffers(1, &mesh->VAO);
+       glBindVertexArray(mesh->vao);
+       glBindBuffer(GL_ARRAY_BUFFER, mesh->vbo);
 
-       free(mesh);
-}
-
-void mesh_configure(Mesh *mesh)
-{
-       glGenVertexArrays(1, &mesh->VAO);
-       glGenBuffers(1, &mesh->VBO);
+       glBufferData(GL_ARRAY_BUFFER, mesh->count * mesh->layout->size,
+               mesh->data, GL_STATIC_DRAW);
 
-       glBindVertexArray(mesh->VAO);
-       glBindBuffer(GL_ARRAY_BUFFER, mesh->VBO);
+       size_t offset = 0;
+       for (GLuint i = 0; i < mesh->layout->count; i++) {
+               VertexAttribute *attrib = &mesh->layout->attributes[i];
 
-       glBufferData(GL_ARRAY_BUFFER, mesh->vertices_count * mesh->layout->size, mesh->vertices, GL_STATIC_DRAW);
+               glVertexAttribPointer(i, attrib->length, attrib->type, GL_FALSE,
+                       mesh->layout->size, (GLvoid *) offset);
+               glEnableVertexAttribArray(i);
 
-       vertex_layout_configure(mesh->layout);
+               offset += attrib->size;
+       }
 
        glBindBuffer(GL_ARRAY_BUFFER, 0);
        glBindVertexArray(0);
 
-       if (mesh->free_vertices)
-               free(mesh->vertices);
+       if (mesh->free_data)
+               free(mesh->data);
 
-       mesh->vertices = NULL;
+       mesh->data = NULL;
 }
 
 void mesh_render(Mesh *mesh)
 {
-       if (mesh->vertices)
-               mesh_configure(mesh);
+       if (mesh->data)
+               mesh_upload(mesh);
+
+       // render
+       glBindVertexArray(mesh->vao);
+       glDrawArrays(GL_TRIANGLES, 0, mesh->count);
+}
+
+void mesh_destroy(Mesh *mesh)
+{
+       if (mesh->data && mesh->free_data)
+               free(mesh->data);
+
+       if (mesh->vao)
+               glDeleteVertexArrays(1, &mesh->vao);
 
-       for (GLuint i = 0; i < mesh->textures_count; i++)
-               glBindTextureUnit(i, mesh->textures[i]);
+       if (mesh->vbo)
+               glDeleteBuffers(1, &mesh->vbo);
 
-       glBindVertexArray(mesh->VAO);
-       glDrawArrays(GL_TRIANGLES, 0, mesh->vertices_count);
+       mesh->vao = mesh->vbo = 0;
 }
index eae283f98dca30a4fb40bc00f2ac6f0c320c6144..3597043a3c02fc181ab7640c2e4f846a7d3e941f 100644 (file)
@@ -4,23 +4,29 @@
 #include <GL/glew.h>
 #include <GL/gl.h>
 #include <stdbool.h>
-#include "client/vertex.h"
 
-typedef struct
-{
-       GLuint VAO, VBO;
-       GLuint *textures;
-       GLuint textures_count;
-       bool free_textures;
-       GLvoid *vertices;
-       GLuint vertices_count;
-       bool free_vertices;
+typedef struct {
+       GLenum type;
+       GLsizei length;
+       GLsizei size;
+} VertexAttribute;
+
+typedef struct {
+       VertexAttribute *attributes;
+       GLuint count;
+       GLsizei size;
+} VertexLayout;
+
+typedef struct {
        VertexLayout *layout;
+       GLuint vao, vbo;
+       GLvoid *data;
+       GLuint count;
+       bool free_data;
 } Mesh;
 
-Mesh *mesh_create();
-void mesh_delete(Mesh *mesh);
-void mesh_configure(Mesh *mesh);
+void mesh_upload(Mesh *mesh);
 void mesh_render(Mesh *mesh);
+void mesh_destroy(Mesh *mesh);
 
-#endif
+#endif // _MESH_H_
diff --git a/src/client/model.c b/src/client/model.c
new file mode 100644 (file)
index 0000000..1d163c7
--- /dev/null
@@ -0,0 +1,542 @@
+#include <dragonstd/tree.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include "client/camera.h"
+#include "client/client_config.h"
+#include "client/frustum.h"
+#include "client/model.h"
+
+typedef struct {
+       GLuint texture;
+       Array vertices;
+} ModelBatchTexture;
+
+static List scene;
+static pthread_rwlock_t lock_scene;
+static GLint units;
+
+// fixme: blending issues still occur
+static int cmp_batch_texture(const ModelBatchTexture *ta, const ModelBatchTexture *tb)
+{
+       return
+               ta->vertices.siz > tb->vertices.siz ? -1 :
+               ta->vertices.siz < tb->vertices.siz ? +1 :
+               0;
+}
+
+static int cmp_node(const ModelNode *node, const char *str)
+{
+       if (str == node->name)
+               return 0;
+
+       if (!node->name)
+               return -1;
+
+       if (!str)
+               return +1;
+
+       return strcmp(node->name, str);
+}
+
+static void transform_node(ModelNode *node)
+{
+       if (node->parent)
+               mat4x4_mul(node->abs, node->parent->abs, node->rel);
+       else
+               mat4x4_dup(node->abs, node->rel);
+
+       list_itr(&node->children, &transform_node, NULL, NULL);
+}
+
+static void render_node(ModelNode *node)
+{
+       if (!node->visible)
+               return;
+
+       for (size_t i = 0; i < node->meshes.siz; i++) {
+               ModelMesh *mesh = &((ModelMesh *) node->meshes.ptr)[i];
+
+               glUseProgram(mesh->shader->prog);
+               glUniformMatrix4fv(mesh->shader->loc_transform, 1, GL_FALSE, node->abs[0]);
+
+               // bind textures
+               for (GLuint i = 0; i < mesh->num_textures; i++)
+                       glBindTextureUnit(i, mesh->textures[i]);
+
+               mesh_render(mesh->mesh);
+       }
+
+       list_itr(&node->children, (void *) &render_node, NULL, NULL);
+}
+
+static void free_node_meshes(ModelNode *node)
+{
+       for (size_t i = 0; i < node->meshes.siz; i++) {
+               ModelMesh *mesh = &((ModelMesh *) node->meshes.ptr)[i];
+
+               mesh_destroy(mesh->mesh);
+               free(mesh->mesh);
+       }
+
+       list_clr(&node->children, (void *) &free_node_meshes, NULL, NULL);
+}
+
+static void delete_node(ModelNode *node)
+{
+       for (size_t i = 0; i < node->meshes.siz; i++) {
+               ModelMesh *mesh = &((ModelMesh *) node->meshes.ptr)[i];
+
+               if (mesh->textures)
+                       free(mesh->textures);
+       }
+       list_clr(&node->children, (void *) &delete_node, NULL, NULL);
+       array_clr(&node->meshes);
+
+       free(node);
+}
+
+static void init_node(ModelNode *node, ModelNode *parent)
+{
+       if ((node->parent = parent))
+               list_apd(&parent->children, node);
+
+       list_ini(&node->children);
+}
+
+static void clone_mesh(ModelMesh *mesh)
+{
+       GLuint *old_textures = mesh->textures;
+       memcpy(mesh->textures = malloc(mesh->num_textures * sizeof *mesh->textures),
+               old_textures, mesh->num_textures * sizeof *mesh->textures);
+}
+
+static ModelNode *clone_node(ModelNode *original, ModelNode *parent)
+{
+       ModelNode *node = malloc(sizeof *node);
+       *node = *original;
+       init_node(node, parent);
+
+       array_cln(&node->meshes, &original->meshes);
+       for (size_t i = 0; i < node->meshes.siz; i++)
+               clone_mesh(&((ModelMesh *) node->meshes.ptr)[i]);
+
+       list_itr(&original->children, (void *) &clone_node, parent, NULL);
+       return node;
+}
+
+static int cmp_model(const Model *model, const f32 *distance)
+{
+       return f32_cmp(&model->distance, distance);
+}
+
+static void render_model(Model *model)
+{
+       if (model->flags.wireframe)
+               glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+
+       render_node(model->root);
+
+       if (model->flags.wireframe)
+               glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+}
+
+// step model help im stuck
+static void model_step(Model *model, Tree *transparent, f64 dtime)
+{
+       if (client_config.view_distance < (model->distance = sqrt(
+                       pow(model->root->pos.x - camera.eye[0], 2) +
+                       pow(model->root->pos.y - camera.eye[1], 2) +
+                       pow(model->root->pos.z - camera.eye[2], 2))))
+               return;
+
+       if (model->callbacks.step)
+               model->callbacks.step(model, dtime);
+
+       if (!model->root->visible)
+               return;
+
+       if (model->flags.frustum_culling && frustum_cull((aabb3f32) {
+                       v3f32_add(model->box.min, model->root->pos),
+                       v3f32_add(model->box.max, model->root->pos)}))
+               return;
+
+       // fixme: if there are multiple objects with the exact same distance, only one is rendered
+       if (model->flags.transparent)
+               tree_add(transparent, &model->distance, model, &cmp_model, NULL);
+       else
+               render_model(model);
+}
+
+// init
+void model_init()
+{
+       list_ini(&scene);
+       pthread_rwlock_init(&lock_scene, NULL);
+       glGetIntegerv(GL_MAX_TEXTURE_UNITS, &units);
+}
+
+// ded
+void model_deinit()
+{
+       list_clr(&scene, &model_delete, NULL, NULL);
+       pthread_rwlock_destroy(&lock_scene);
+}
+
+// Model functions
+
+Model *model_create()
+{
+       Model *model = malloc(sizeof *model);
+       model->root = model_node_create(NULL);
+       model->extra = NULL;
+
+       model->callbacks.step = NULL;
+       model->callbacks.delete = NULL;
+
+       model->flags.delete =
+               model->flags.wireframe =
+               model->flags.frustum_culling =
+               model->flags.transparent = 0;
+
+       return model;
+}
+
+Model *model_load(const char *path, const char *textures_path, Mesh *cube, ModelShader *shader)
+{
+       Model *model = model_create();
+
+       Array stack;
+       array_ini(&stack, sizeof(ModelNode *), 5);
+       array_apd(&stack, &model->root);
+
+       FILE *file = fopen(path, "r");
+       if (!file) {
+               fprintf(stderr, "[warning] failed to open model %s\n", path);
+               return NULL;
+       }
+
+       char *line = NULL;
+       size_t siz = 0;
+       ssize_t length;
+       int count = 0;
+
+       while ((length = getline(&line, &siz, file)) > 0) {
+               count++;
+
+               if (*line == '#')
+                       continue;
+
+               char *cursor = line - 1;
+
+               size_t tabs = 0;
+               while (*++cursor == '\t')
+                       tabs++;
+
+               if (tabs >= stack.siz) {
+                       fprintf(stderr, "[warning] invalid indent in model %s in line %d\n", path, count);
+                       continue;
+               }
+
+               ModelNode *node = model_node_create(((ModelNode **) stack.ptr)[tabs]);
+
+               int n;
+               char key[length + 1];
+               while (sscanf(cursor, "%s %n", key, &n) == 1) {
+                       cursor += n;
+
+                       if (strcmp(key, "name") == 0) {
+                               char name[length + 1];
+
+                               if (sscanf(cursor, "%s %n", name, &n) == 1) {
+                                       cursor += n;
+
+                                       if (node->name)
+                                               free(node->name);
+                                       node->name = strdup(name);
+                               } else {
+                                       fprintf(stderr, "[warning] invalid value for name in model %s in line %d\n", path, count);
+                               }
+                       } else if (strcmp(key, "pos") == 0) {
+                               if (sscanf(cursor, "%f %f %f %n", &node->pos.x, &node->pos.y, &node->pos.z, &n) == 3)
+                                       cursor += n;
+                               else
+                                       fprintf(stderr, "[warning] invalid value for pos in model %s in line %d\n", path, count);
+                       } else if (strcmp(key, "rot") == 0) {
+                               if (sscanf(cursor, "%f %f %f %n", &node->rot.x, &node->rot.y, &node->rot.z, &n) == 3)
+                                       cursor += n;
+                               else
+                                       fprintf(stderr, "[warning] invalid value for rot in model %s in line %d\n", path, count);
+                       } else if (strcmp(key, "scale") == 0) {
+                               if (sscanf(cursor, "%f %f %f %n", &node->scale.x, &node->scale.y, &node->scale.z, &n) == 3)
+                                       cursor += n;
+                               else
+                                       fprintf(stderr, "[warning] invalid value for scale in model %s in line %d\n", path, count);
+                       } else if (strcmp(key, "angle") == 0) {
+                               if (sscanf(cursor, "%f%n", &node->angle, &n) == 1)
+                                       cursor += n;
+                               else
+                                       fprintf(stderr, "[warning] invalid value for angle in model %s in line %d\n", path, count);
+                       } else if (strcmp(key, "cube") == 0) {
+                               char texture[length + 1];
+
+                               if (sscanf(cursor, "%s %n", texture, &n) == 1) {
+                                       cursor += n;
+
+                                       char filepath[strlen(textures_path) + 1 + strlen(texture) + 1];
+                                       sprintf(filepath, "%s/%s", textures_path, texture);
+                                       Texture *texture = texture_load_cubemap(filepath);
+
+                                       model_node_add_mesh(node, &(ModelMesh) {
+                                               .mesh = cube,
+                                               .textures = &texture->txo,
+                                               .num_textures = 1,
+                                               .shader = shader,
+                                       });
+                               } else {
+                                       fprintf(stderr, "[warning] invalid value for cube in model %s in line %d\n", path, count);
+                               }
+                       } else {
+                               fprintf(stderr, "[warning] invalid key '%s' in model %s in line %d\n", key, path, count);
+                       }
+               }
+
+               model_node_transform(node);
+
+               stack.siz = tabs + 1;
+               array_apd(&stack, &node);
+       }
+
+       if (line)
+               free(line);
+
+       fclose(file);
+       array_clr(&stack);
+
+       return model;
+}
+
+Model *model_clone(Model *original)
+{
+       Model *model = malloc(sizeof *model);
+       *model = *original;
+       model->root = clone_node(original->root, NULL);
+       return model;
+}
+
+void model_delete(Model *model)
+{
+       if (model->callbacks.delete)
+               model->callbacks.delete(model);
+
+       delete_node(model->root);
+       free(model);
+}
+
+void model_free_meshes(Model *model)
+{
+       free_node_meshes(model->root);
+}
+
+void model_get_bones(Model *model, ModelBoneMapping *mappings, size_t num_mappings)
+{
+       char *name, *cursor, *saveptr, *tok;
+
+       for (size_t i = 0; i < num_mappings; i++) {
+               name = cursor = strdup(mappings[i].name);
+
+               ModelNode *node = model->root;
+               while ((tok = strtok_r(cursor, ".", &saveptr))) {
+                       node = list_get(&node->children, tok, (void *) &cmp_node, NULL);
+                       cursor = NULL;
+               }
+
+               if (node)
+                       *mappings[i].node = node;
+               else
+                       fprintf(stderr, "[warning] no such bone: %s\n", name);
+
+               free(name);
+       }
+}
+
+// ModelNode functions
+
+ModelNode *model_node_create(ModelNode *parent)
+{
+       ModelNode *node = malloc(sizeof *node);
+       node->name = NULL;
+       node->visible = true;
+       node->pos = (v3f32) {0.0f, 0.0f, 0.0f};
+       node->rot = (v3f32) {0.0f, 0.0f, 0.0f};
+       node->scale = (v3f32) {1.0f, 1.0f, 1.0f};
+       node->angle = 0.0f;
+       array_ini(&node->meshes, sizeof(ModelMesh), 0);
+       init_node(node, parent);
+       return node;
+}
+
+void model_node_transform(ModelNode *node)
+{
+       mat4x4_translate(node->rel,
+               node->pos.x,
+               node->pos.y,
+               node->pos.z);
+
+       mat4x4_rotate(node->rel, node->rel,
+               node->rot.x,
+               node->rot.y,
+               node->rot.z,
+               node->angle);
+
+       mat4x4_scale_aniso(node->rel, node->rel,
+               node->scale.x,
+               node->scale.y,
+               node->scale.z);
+
+       transform_node(node);
+}
+
+void model_node_add_mesh(ModelNode *node, const ModelMesh *mesh)
+{
+       array_apd(&node->meshes, mesh);
+       clone_mesh(&((ModelMesh *) node->meshes.ptr)[node->meshes.siz - 1]);
+}
+
+void model_node_add_batch(ModelNode *node, ModelBatch *batch)
+{
+       if (!batch->textures.siz) {
+               free(batch);
+               return;
+       }
+
+       array_srt(&batch->textures, &cmp_batch_texture);
+       ModelBatchTexture *textures = batch->textures.ptr;
+
+       size_t num_meshes = ceil((double) batch->textures.siz / (double) units);
+       array_grw(&node->meshes, num_meshes);
+       ModelMesh *meshes = node->meshes.ptr + node->meshes.siz - num_meshes;
+
+       for (size_t m = 0; m < num_meshes; m++) {
+               ModelMesh *mesh = &meshes[m];
+
+               mesh->mesh = malloc(sizeof *mesh->mesh);
+               mesh->mesh->layout = batch->layout;
+               mesh->mesh->vao = mesh->mesh->vbo = 0;
+               mesh->mesh->free_data = true;
+
+               mesh->textures = malloc(sizeof *mesh->textures * (mesh->num_textures =
+                       ceil((double) (batch->textures.siz - m) / (double) num_meshes)));
+
+               mesh->shader = batch->shader;
+
+               mesh->mesh->count = 0;
+               for (size_t t = 0; t < mesh->num_textures; t++) {
+                       ModelBatchTexture *texture = &textures[m + t * num_meshes];
+                       mesh->mesh->count += texture->vertices.siz;
+                       mesh->textures[t] = texture->texture;
+
+                       for (size_t v = 0; v < texture->vertices.siz; v++)
+                               *((f32 *) (texture->vertices.ptr + v * texture->vertices.mbs
+                                       + batch->off_tex_idx)) = t;
+               }
+
+               ModelBatchTexture *first = &textures[m];
+               first->vertices.cap = mesh->mesh->count;
+               first->vertices.ext = 0;
+               array_rlc(&first->vertices);
+
+               mesh->mesh->data = first->vertices.ptr;
+
+               for (size_t t = 1; t < mesh->num_textures; t++) {
+                       ModelBatchTexture *texture = &textures[m + t * num_meshes];
+                       memcpy(first->vertices.ptr + first->vertices.siz * first->vertices.mbs,
+                               texture->vertices.ptr, texture->vertices.siz * texture->vertices.mbs);
+                       first->vertices.siz += texture->vertices.siz;
+
+                       array_clr(&texture->vertices);
+               }
+       }
+
+       array_clr(&batch->textures);
+       free(batch);
+}
+
+// ModelBatch functions
+
+ModelBatch *model_batch_create(ModelShader *shader, VertexLayout *layout, size_t off_tex_idx)
+{
+       ModelBatch *batch = malloc(sizeof *batch);
+       batch->shader = shader;
+       batch->layout = layout;
+       batch->off_tex_idx = off_tex_idx;
+       array_ini(&batch->textures, sizeof(ModelBatchTexture), 5);
+       return batch;
+}
+
+void model_batch_free(ModelBatch *batch)
+{
+       for (size_t i = 0; i < batch->textures.siz; i++)
+               array_clr(&((ModelBatchTexture *) batch->textures.ptr)[i].vertices);
+
+       array_clr(&batch->textures);
+       free(batch);
+}
+
+void model_batch_add_vertex(ModelBatch *batch, GLuint texture, const void *vertex)
+{
+       ModelBatchTexture *batch_texture = NULL;
+
+       for (size_t i = 0; i <= batch->textures.siz; i++) {
+               if (i == batch->textures.siz) {
+                       ModelBatchTexture new;
+                       new.texture = texture;
+                       array_ini(&new.vertices, batch->layout->size, 10000);
+                       array_apd(&batch->textures, &new);
+               }
+
+               if ((batch_texture = &((ModelBatchTexture *) batch->textures.ptr)[i])->texture == texture)
+                       break;
+       }
+
+       array_apd(&batch_texture->vertices, vertex);
+}
+
+// scene functions
+
+void model_scene_add(Model *model)
+{
+       pthread_rwlock_wrlock(&lock_scene);
+       list_apd(&scene, model);
+       pthread_rwlock_unlock(&lock_scene);
+}
+
+void model_scene_render(f64 dtime)
+{
+       Tree transparent;
+       tree_ini(&transparent);
+
+       pthread_rwlock_rdlock(&lock_scene);
+       for (ListNode **node = &scene.fst; *node != NULL;) {
+               Model *model = (*node)->dat;
+
+               pthread_rwlock_unlock(&lock_scene);
+               if (model->flags.delete) {
+                       model_delete(model);
+
+                       pthread_rwlock_wrlock(&lock_scene);
+                       list_nrm(&scene, node);
+                       pthread_rwlock_unlock(&lock_scene);
+
+                       pthread_rwlock_rdlock(&lock_scene);
+               } else {
+                       model_step(model, &transparent, dtime);
+
+                       pthread_rwlock_rdlock(&lock_scene);
+                       node = &(*node)->nxt;
+               }
+       }
+       pthread_rwlock_unlock(&lock_scene);
+
+       tree_clr(&transparent, &render_model, NULL, NULL, TRAVERSION_INORDER);
+}
diff --git a/src/client/model.h b/src/client/model.h
new file mode 100644 (file)
index 0000000..663df77
--- /dev/null
@@ -0,0 +1,106 @@
+#ifndef _MODEL_H_
+#define _MODEL_H_
+
+#include <dragonstd/array.h>
+#include <dragonstd/list.h>
+#include <GL/glew.h>
+#include <GL/gl.h>
+#include <linmath.h/linmath.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include "client/mesh.h"
+#include "client/texture.h"
+#include "types.h"
+
+typedef struct {
+       GLuint prog;
+       GLint loc_transform;
+} ModelShader;
+
+typedef struct {
+       ModelShader *shader;
+       VertexLayout *layout;
+       size_t off_tex_idx;
+       Array textures;
+} ModelBatch;
+
+typedef struct ModelNode {
+       char *name;
+       bool visible;
+       v3f32 pos, rot, scale;
+       f32 angle;
+       mat4x4 abs, rel;
+       Array meshes;
+       struct ModelNode *parent;
+       List children;
+} ModelNode;
+
+typedef struct {
+       Mesh *mesh;
+       GLuint *textures;
+       GLuint num_textures;
+       ModelShader *shader;
+} ModelMesh;
+
+typedef struct {
+       char *name;
+       ModelNode **node;
+} ModelBoneMapping;
+
+typedef struct Model {
+       ModelNode *root;
+       void *extra;
+       aabb3f32 box;
+       f32 distance;
+       struct {
+               void (*step)(struct Model *model, f64 dtime);
+               void (*delete)(struct Model *model);
+       } callbacks;
+       struct {
+               unsigned int delete: 1;
+               unsigned int wireframe: 1;
+               unsigned int frustum_culling: 1;
+               unsigned int transparent: 1;
+       } flags;
+} Model;
+
+// initialize
+void model_init();
+// ded
+void model_deinit();
+
+// create empty model
+Model *model_create();
+// load model from file
+Model *model_load(const char *path, const char *textures_path, Mesh *cube, ModelShader *shader);
+// clone existing model
+Model *model_clone(Model *model);
+// delete model
+void model_delete(Model *model);
+// use this as delete callback to free all meshes associated with the model on delete
+void model_free_meshes(Model *model);
+// get bone locations
+void model_get_bones(Model *model, ModelBoneMapping *mappings, size_t num_mappings);
+
+// add a new mode
+ModelNode *model_node_create(ModelNode *parent);
+// recalculate transform matrices
+void model_node_transform(ModelNode *node);
+// add a mesh to model node (will be copied)
+void model_node_add_mesh(ModelNode *node, const ModelMesh *mesh);
+// add and delete batch (may add multiple meshes depending on available texture units)
+void model_node_add_batch(ModelNode *node, ModelBatch *batch);
+
+// create batch
+ModelBatch *model_batch_create(ModelShader *shader, VertexLayout *layout, size_t off_tex_idx);
+// delete batch
+void model_batch_free(ModelBatch *batch);
+// add a vertex to batch
+void model_batch_add_vertex(ModelBatch *batch, GLuint texture, const void *vertex);
+
+// add model to scene
+void model_scene_add(Model *model);
+// render scene
+void model_scene_render(f64 dtime);
+
+#endif // _MODEL_H_
diff --git a/src/client/object.c b/src/client/object.c
deleted file mode 100644 (file)
index 247d985..0000000
+++ /dev/null
@@ -1,213 +0,0 @@
-#include <stdio.h>
-#include <stdlib.h>
-#include "client/frustum.h"
-#include "client/object.h"
-#include "client/scene.h"
-#define OBJECT_VERTEX_ATTRIBUTES 5
-
-static VertexAttribute vertex_attributes[OBJECT_VERTEX_ATTRIBUTES] = {
-       // position
-       {
-               .type = GL_FLOAT,
-               .length = 3,
-               .size = sizeof(Vertex3DPosition),
-       },
-       // normal
-       {
-               .type = GL_FLOAT,
-               .length = 3,
-               .size = sizeof(Vertex3DNormal),
-       },
-       // textureIndex
-       {
-               .type = GL_FLOAT,
-               .length = 1,
-               .size = sizeof(Vertex3DTextureIndex),
-       },
-       // textureCoordinates
-       {
-               .type = GL_FLOAT,
-               .length = 2,
-               .size = sizeof(Vertex3DTextureCoordinates),
-
-       },
-       // color
-       {
-               .type = GL_FLOAT,
-               .length = 3,
-               .size = sizeof(Vertex3DColor),
-       },
-};
-
-static VertexLayout vertex_layout = {
-       .attributes = vertex_attributes,
-       .count = OBJECT_VERTEX_ATTRIBUTES,
-       .size = sizeof(Vertex3D),
-};
-
-Object *object_create()
-{
-       Object *obj = malloc(sizeof(Object));
-       obj->pos = (v3f32) {0.0f, 0.0f, 0.0f};
-       obj->rot = (v3f32) {0.0f, 0.0f, 0.0f};
-       obj->scale = (v3f32) {1.0f, 1.0f, 1.0f};
-       obj->angle = 0.0f;
-       obj->remove = false;
-       obj->meshes = NULL;
-       obj->meshes_count = 0;
-       obj->visible = true;
-       obj->wireframe = false;
-       obj->frustum_culling = false;
-       obj->transparent = false;
-       obj->box = (aabb3f32) {{0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f}};
-       obj->current_face = NULL;
-       obj->faces = array_create(sizeof(ObjectFace));
-       obj->on_render = NULL;
-       obj->extra = NULL;
-
-       return obj;
-}
-
-void object_delete(Object *obj)
-{
-       for (size_t i = 0; i < obj->meshes_count; i++)
-               mesh_delete(obj->meshes[i]);
-
-       free(obj);
-}
-
-void object_set_texture(Object *obj, Texture *texture)
-{
-       if (obj->current_face && obj->current_face->texture == texture->id)
-               return;
-
-       ObjectFace face = {
-               .texture = texture->id,
-               .vertices = array_create(sizeof(Vertex3D)),
-       };
-
-       array_append(&obj->faces, &face);
-       obj->current_face = &((ObjectFace *) obj->faces.ptr)[obj->faces.siz - 1];
-}
-
-void object_add_vertex(Object *obj, Vertex3D *vertex)
-{
-       array_append(&obj->current_face->vertices, vertex);
-}
-
-static int qsort_compare_faces(const void *f1, const void *f2)
-{
-       return ((ObjectFace *) f1)->texture - ((ObjectFace *) f2)->texture;
-}
-
-static void add_mesh(Array *meshes, Array *vertices, Array *textures)
-{
-       if (vertices->siz > 0) {
-               Mesh *mesh = mesh_create();
-               mesh->vertices = vertices->ptr;
-               mesh->vertices_count = vertices->siz;
-               mesh->free_vertices = true;
-               mesh->free_textures = true;
-               mesh->layout = &vertex_layout;
-               size_t textures_count;
-               array_copy(textures, (void *) &mesh->textures, &textures_count);
-               mesh->textures_count = textures_count;
-
-               free(textures->ptr);
-
-               array_append(meshes, &mesh);
-       }
-
-       *vertices = array_create(sizeof(Vertex3D));
-       *textures = array_create(sizeof(GLuint));
-}
-
-bool object_add_to_scene(Object *obj)
-{
-       if (obj->faces.siz == 0)
-               return false;
-
-       object_transform(obj);
-
-       qsort(obj->faces.ptr, obj->faces.siz, sizeof(ObjectFace), &qsort_compare_faces);
-
-       GLuint max_texture_units = scene_get_max_texture_units();
-
-       Array meshes = array_create(sizeof(Mesh *));
-
-       Array vertices = array_create(sizeof(Vertex3D));
-       Array textures = array_create(sizeof(GLuint));
-
-       GLuint texture_id = 0;
-       GLuint texture_index = max_texture_units;
-
-       for (size_t f = 0; f < obj->faces.siz; f++) {
-               ObjectFace *face = &((ObjectFace *) obj->faces.ptr)[f];
-
-               if (face->texture != texture_id) {
-                       if (++texture_index >= max_texture_units) {
-                               add_mesh(&meshes, &vertices, &textures);
-                               texture_index = 0;
-                       }
-
-                       texture_id = face->texture;
-                       array_append(&textures, &texture_id);
-               }
-
-               for (size_t v = 0; v < face->vertices.siz; v++) {
-                       Vertex3D *vertex = &((Vertex3D *) face->vertices.ptr)[v];
-                       vertex->textureIndex = texture_index;
-                       array_append(&vertices, vertex);
-               }
-
-               free(face->vertices.ptr);
-       }
-       add_mesh(&meshes, &vertices, &textures);
-       free(obj->faces.ptr);
-
-       array_copy(&meshes, (void *) &obj->meshes, &obj->meshes_count);
-       free(meshes.ptr);
-
-       scene_add_object(obj);
-
-       return true;
-}
-
-void object_transform(Object *obj)
-{
-       mat4x4_translate(obj->transform, obj->pos.x, obj->pos.y, obj->pos.z);
-       mat4x4_rotate(obj->transform, obj->transform, obj->rot.x, obj->rot.y, obj->rot.z, obj->angle);
-       mat4x4_scale_aniso(obj->transform, obj->transform, obj->scale.x, obj->scale.y, obj->scale.z);
-}
-
-bool object_before_render(Object *obj, f64 dtime)
-{
-       if (obj->on_render)
-               obj->on_render(obj, dtime);
-
-       if (! obj->visible)
-               return false;
-
-       if (obj->frustum_culling) {
-               aabb3f32 box = {v3f32_add(obj->box.min, obj->pos), v3f32_add(obj->box.max, obj->pos)};
-
-                if (! frustum_is_visible(box))
-                       return false;
-       }
-
-       return true;
-}
-
-void object_render(Object *obj)
-{
-       glUniformMatrix4fv(scene.loc_model, 1, GL_FALSE, obj->transform[0]);
-
-       if (obj->wireframe)
-               glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
-
-       for (size_t i = 0; i < obj->meshes_count; i++)
-               mesh_render(obj->meshes[i]);
-
-       if (obj->wireframe)
-               glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
-}
diff --git a/src/client/object.h b/src/client/object.h
deleted file mode 100644 (file)
index a5152d2..0000000
+++ /dev/null
@@ -1,76 +0,0 @@
-#ifndef _OBJECT_H_
-#define _OBJECT_H_
-
-#include <stddef.h>
-#include <stdbool.h>
-#include <GL/glew.h>
-#include <GL/gl.h>
-#include <linmath.h/linmath.h>
-#include <dragonstd/array.h>
-#include "client/mesh.h"
-#include "client/texture.h"
-#include "client/vertex.h"
-#include "types.h"
-
-typedef struct {
-       GLfloat x, y, z;
-} __attribute__((packed)) Vertex3DPosition;
-
-typedef struct {
-       GLfloat x, y, z;
-} __attribute__((packed)) Vertex3DNormal;
-
-typedef GLfloat Vertex3DTextureIndex;
-
-typedef struct {
-       GLfloat s, t;
-} __attribute__((packed)) Vertex3DTextureCoordinates;
-
-typedef struct {
-       GLfloat r, g, b;
-} __attribute__((packed)) Vertex3DColor;
-
-typedef struct
-{
-       Vertex3DPosition position;
-       Vertex3DNormal normal;
-       Vertex3DTextureIndex textureIndex;
-       Vertex3DTextureCoordinates textureCoordinates;
-       Vertex3DColor color;
-} __attribute__((packed)) Vertex3D;
-
-typedef struct
-{
-       GLuint texture;
-       Array vertices;
-} ObjectFace;
-
-typedef struct Object
-{
-       v3f32 pos, rot, scale;
-       f32 angle;
-       bool remove;
-       Mesh **meshes;
-       size_t meshes_count;
-       mat4x4 transform;
-       bool visible;
-       bool wireframe;
-       bool frustum_culling;
-       bool transparent;
-       aabb3f32 box;
-       ObjectFace *current_face;
-       Array faces;
-       void (*on_render)(struct Object *obj, f64 dtime);
-       void *extra;
-} Object;
-
-Object *object_create();
-void object_delete(Object *obj);
-void object_set_texture(Object *obj, Texture *texture);
-void object_add_vertex(Object *obj, Vertex3D *vertex);
-bool object_add_to_scene(Object *obj);
-void object_transform(Object *obj);
-bool object_before_render(Object *obj, f64 dtime);
-void object_render(Object *obj);
-
-#endif
diff --git a/src/client/scene.c b/src/client/scene.c
deleted file mode 100644 (file)
index d2d42ca..0000000
+++ /dev/null
@@ -1,144 +0,0 @@
-#include <stdlib.h>
-#include <stdio.h>
-#include "client/camera.h"
-#include "client/client.h"
-#include "client/client_config.h"
-#include "client/frustum.h"
-#include "client/scene.h"
-#include "client/shader.h"
-#include "day.h"
-#include "util.h"
-
-struct Scene scene;
-
-static int bintree_compare_f32(void *v1, void *v2, unused Bintree *tree)
-{
-       f32 diff = (*(f32 *) v2) - (*(f32 *) v1);
-       return CMPBOUNDS(diff);
-}
-
-bool scene_init()
-{
-       scene.objects = list_create(NULL);
-       scene.transparent_objects = bintree_create(sizeof(f32), &bintree_compare_f32);
-       pthread_mutex_init(&scene.mtx, NULL);
-
-       glGetIntegerv(GL_MAX_TEXTURE_UNITS, &scene.max_texture_units);
-
-       char *shader_defs = format_string(
-               "#define MAX_TEXTURE_UNITS %d\n"
-               "#define RENDER_DISTANCE %lf\n",
-               scene.max_texture_units,
-               client_config.render_distance
-       );
-
-       if (! shader_program_create(RESSOURCE_PATH "shaders/3d", &scene.prog, shader_defs)) {
-               fprintf(stderr, "Failed to create 3D shader program\n");
-               return false;
-       }
-
-       free(shader_defs);
-
-       scene.loc_model = glGetUniformLocation(scene.prog, "model");
-       scene.loc_VP = glGetUniformLocation(scene.prog, "VP");
-       scene.loc_daylight = glGetUniformLocation(scene.prog, "daylight");
-       scene.loc_fogColor = glGetUniformLocation(scene.prog, "fogColor");
-       scene.loc_ambientLight = glGetUniformLocation(scene.prog, "ambientLight");
-       scene.loc_lightDir = glGetUniformLocation(scene.prog, "lightDir");
-       scene.loc_cameraPos = glGetUniformLocation(scene.prog, "cameraPos");
-
-       GLint texture_indices[scene.max_texture_units];
-       for (GLint i = 0; i < scene.max_texture_units; i++)
-               texture_indices[i] = i;
-
-       glProgramUniform1iv(scene.prog, glGetUniformLocation(scene.prog, "textures"), scene.max_texture_units, texture_indices);
-
-       scene.fov = 86.1f;
-
-       return true;
-}
-
-static void list_delete_object(void *key, unused void *value, unused void *arg)
-{
-       object_delete(key);
-}
-
-void scene_deinit()
-{
-       list_clear_func(&scene.objects, &list_delete_object, NULL);
-       pthread_mutex_destroy(&scene.mtx);
-       glDeleteProgram(scene.prog);
-}
-
-void scene_add_object(Object *obj)
-{
-       pthread_mutex_lock(&scene.mtx);
-       list_put(&scene.objects, obj, NULL);
-       pthread_mutex_unlock(&scene.mtx);
-}
-
-static void bintree_render_object(BintreeNode *node, unused void *arg)
-{
-       object_render(node->value);
-}
-
-void scene_render(f64 dtime)
-{
-       mat4x4_mul(scene.VP, scene.projection, camera.view);
-
-       vec4 base_sunlight_dir = {0.0f, 0.0f, -1.0f, 1.0f};
-       vec4 sunlight_dir;
-       mat4x4 sunlight_mat;
-       mat4x4_identity(sunlight_mat);
-
-       mat4x4_rotate(sunlight_mat, sunlight_mat, 1.0f, 0.0f, 0.0f, get_sun_angle() + M_PI / 2.0f);
-       mat4x4_mul_vec4(sunlight_dir, sunlight_mat, base_sunlight_dir);
-
-       frustum_update(scene.VP);
-
-       f32 daylight = get_daylight();
-       f32 ambient_light = f32_mix(0.3f, 0.7f, daylight);
-       v3f32 fog_color = v3f32_mix((v3f32) {0x03, 0x0A, 0x1A}, (v3f32) {0x87, 0xCE, 0xEB}, daylight);
-
-       glUseProgram(scene.prog);
-       glUniformMatrix4fv(scene.loc_VP, 1, GL_FALSE, scene.VP[0]);
-       glUniform3f(scene.loc_lightDir, sunlight_dir[0], sunlight_dir[1], sunlight_dir[2]);
-       glUniform3f(scene.loc_cameraPos, camera.eye[0], camera.eye[1], camera.eye[2]);
-       glUniform1f(scene.loc_daylight, daylight);
-       glUniform3f(scene.loc_fogColor, fog_color.x / 0xFF * ambient_light, fog_color.y / 0xFF * ambient_light, fog_color.z / 0xFF * ambient_light);
-       glUniform1f(scene.loc_ambientLight, ambient_light);
-
-       for (ListPair **pairptr = &scene.objects.first; *pairptr != NULL; ) {
-               ListPair *pair = *pairptr;
-               Object *obj = pair->key;
-               if (obj->remove) {
-                       pthread_mutex_lock(&scene.mtx);
-                       *pairptr = pair->next;
-                       pthread_mutex_unlock(&scene.mtx);
-                       free(pair);
-                       object_delete(obj);
-               } else {
-                       f32 distance = sqrt(pow(obj->pos.x - camera.eye[0], 2) + pow(obj->pos.y - camera.eye[1], 2) + pow(obj->pos.z - camera.eye[2], 2));
-                       if (distance < client_config.render_distance && object_before_render(obj, dtime)) {
-                               if (obj->transparent)
-                                       bintree_insert(&scene.transparent_objects, &distance, obj);
-                               else
-                                       object_render(obj);
-                       }
-                       pairptr = &pair->next;
-               }
-       }
-
-       bintree_traverse(&scene.transparent_objects, BTT_INORDER, &bintree_render_object, NULL);
-       bintree_clear(&scene.transparent_objects, NULL, NULL);
-}
-
-void scene_on_resize(int width, int height)
-{
-       mat4x4_perspective(scene.projection, scene.fov / 180.0f * M_PI, (float) width / (float) height, 0.01f, client_config.render_distance + 28.0f);
-}
-
-GLuint scene_get_max_texture_units()
-{
-       return scene.max_texture_units;
-}
diff --git a/src/client/scene.h b/src/client/scene.h
deleted file mode 100644 (file)
index 49a786d..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-#ifndef _SCENE_H_
-#define _SCENE_H_
-
-#include <stdbool.h>
-#include <pthread.h>
-#include <linmath.h/linmath.h>
-#include <dragonstd/bintree.h>
-#include <dragonstd/list.h>
-#include "types.h"
-#include "client/object.h"
-
-extern struct Scene
-{
-       List objects;
-       Bintree transparent_objects;
-       pthread_mutex_t mtx;
-       GLuint prog;
-       GLint loc_model;
-       GLint loc_VP;
-       GLint loc_daylight;
-       GLint loc_fogColor;
-       GLint loc_ambientLight;
-       GLint loc_lightDir;
-       GLint loc_cameraPos;
-       GLint max_texture_units;
-       mat4x4 VP;
-       mat4x4 projection;
-       f32 fov;
-       f32 render_distance;
-} scene;
-
-bool scene_init();
-void scene_deinit();
-void scene_add_object(Object *obj);
-void scene_render(f64 dtime);
-void scene_on_resize(int width, int height);
-GLuint scene_get_max_texture_units();
-void scene_get_view_proj(mat4x4 target);
-
-#endif
index 533ecc8a35f641b819edd908c4ce8f08aff240b5..f4f050f2a0ab5a684738f4435e3b708355a64502 100644 (file)
@@ -1,16 +1,16 @@
-#include <string.h>
 #include <stdio.h>
-#include <unistd.h>
 #include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
 #include "client/shader.h"
 
-static GLuint compile_and_attach_shader(GLenum type, const char *path, const char *name, GLuint program, const char *definitions)
+static GLuint compile_shader(GLenum type, const char *path, const char *name, GLuint program, const char *definitions)
 {
        char full_path[strlen(path) + 1 + strlen(name) + 1 + 4 + 1];
        sprintf(full_path, "%s/%s.glsl", path, name);
 
        FILE *file = fopen(full_path, "r");
-       if (! file) {
+       if (!file) {
                perror("fopen");
                return 0;
        }
@@ -47,7 +47,8 @@ static GLuint compile_and_attach_shader(GLenum type, const char *path, const cha
 
        GLuint id = glCreateShader(type);
 
-       const char *version = "#version 420 core\n";
+       // Minimum OpenGL version is 4.2.0 (idk some shader feature from that version is required)
+       const char *version = "#version 420 core\n"; // 420 blaze it
 
        const char *code_list[3] = {
                version,
@@ -67,10 +68,10 @@ static GLuint compile_and_attach_shader(GLenum type, const char *path, const cha
 
        GLint success;
        glGetShaderiv(id, GL_COMPILE_STATUS, &success);
-       if (! success) {
+       if (!success) {
                char errbuf[BUFSIZ];
                glGetShaderInfoLog(id, BUFSIZ, NULL, errbuf);
-               fprintf(stderr, "Failed to compile %s shader: %s", name, errbuf);
+               fprintf(stderr, "[error] failed to compile %s shader: %s", name, errbuf);
                glDeleteShader(id);
                return 0;
        }
@@ -84,17 +85,17 @@ bool shader_program_create(const char *path, GLuint *idptr, const char *definiti
 {
        GLuint id = glCreateProgram();
 
-       if (! definitions)
+       if (!definitions)
                definitions = "";
 
        GLuint vert, frag;
 
-       if (! (vert = compile_and_attach_shader(GL_VERTEX_SHADER, path, "vertex", id, definitions))) {
+       if (!(vert = compile_shader(GL_VERTEX_SHADER, path, "vertex", id, definitions))) {
                glDeleteProgram(id);
                return false;
        }
 
-       if (! (frag = compile_and_attach_shader(GL_FRAGMENT_SHADER, path, "fragment", id, definitions))) {
+       if (!(frag = compile_shader(GL_FRAGMENT_SHADER, path, "fragment", id, definitions))) {
                glDeleteShader(vert);
                glDeleteProgram(id);
                return false;
@@ -106,10 +107,10 @@ bool shader_program_create(const char *path, GLuint *idptr, const char *definiti
 
        GLint success;
        glGetProgramiv(id, GL_LINK_STATUS, &success);
-       if (! success) {
+       if (!success) {
                char errbuf[BUFSIZ];
                glGetProgramInfoLog(id, BUFSIZ, NULL, errbuf);
-               fprintf(stderr, "Failed to link shader program: %s\n", errbuf);
+               fprintf(stderr, "[error] failed to link shader program: %s\n", errbuf);
                glDeleteProgram(id);
                return false;
        }
index cfc1abb70722b6e3f28b9962b791452ba2ff36bf..eb62941f565eb90ca8283e1970d6866563e8cf35 100644 (file)
@@ -1,10 +1,10 @@
 #ifndef _SHADER_H_
 #define _SHADER_H_
 
-#include <stdbool.h>
 #include <GL/glew.h>
 #include <GL/gl.h>
+#include <stdbool.h>
 
 bool shader_program_create(const char *path, GLuint *idptr, const char *definitions);
 
-#endif
+#endif // _SHADER_H_
index 2867298a600c4c8d45a424bff1d3eec5891d4506..45b78365ccf20ee7de9f05a3d69b02f80d30b968 100644 (file)
 #include "client/client.h"
 #include "client/cube.h"
 #include "client/mesh.h"
-#include "client/scene.h"
 #include "client/shader.h"
 #include "client/sky.h"
 #include "client/texture.h"
+#include "client/window.h"
 #include "day.h"
 
-static struct
-{
-       GLuint skybox_prog;
-       GLint skybox_loc_VP;
-       GLint skybox_loc_daylight;
-       GLuint skybox_textures[2];
-       Mesh *skybox_mesh;
-       GLuint sun_prog;
-       GLint sun_loc_MVP;
-       Texture *sun_texture;
-       Mesh *sun_mesh;
-       GLuint clouds_prog;
-       GLint clouds_loc_VP;
-       GLint clouds_loc_daylight;
-       Mesh *clouds_mesh;
-} sky;
-
-typedef struct
-{
-       GLfloat x, y, z;
-} __attribute__((packed)) VertexSunPosition;
-
-typedef struct
-{
-       GLfloat s, t;
-} __attribute__((packed)) VertexSunTextureCoordinates;
-
-typedef struct
-{
-       VertexSunPosition position;
-       VertexSunTextureCoordinates textureCoordinates;
-} __attribute__((packed)) VertexSun;
-
-static VertexAttribute sun_vertex_attributes[2] = {
-       // position
-       {
-               .type = GL_FLOAT,
-               .length = 3,
-               .size = sizeof(VertexSunPosition),
+static GLuint sun_prog;
+static GLint sun_loc_MVP;
+static GLuint sun_texture;
+typedef struct {
+       v3f32 position;
+       v2f32 textureCoordinates;
+} __attribute__((packed)) SunVertex;
+static Mesh sun_mesh = {
+       .layout = &(VertexLayout) {
+               .attributes = (VertexAttribute[]) {
+                       {GL_FLOAT, 3, sizeof(v3f32)}, // position
+                       {GL_FLOAT, 2, sizeof(v2f32)}, // textureCoordinates
+               },
+               .count = 2,
+               .size = sizeof(SunVertex),
        },
-       // textureCoordinates
-       {
-               .type = GL_FLOAT,
-               .length = 2,
-               .size = sizeof(VertexSunTextureCoordinates),
+       .vao = 0,
+       .vbo = 0,
+       .data = (SunVertex[]) {
+               {{-0.5, -0.5, +0.0}, {+0.0, +0.0}},
+               {{+0.5, -0.5, +0.0}, {+1.0, +0.0}},
+               {{+0.5, +0.5, +0.0}, {+1.0, +1.0}},
+               {{+0.5, +0.5, +0.0}, {+1.0, +1.0}},
+               {{-0.5, +0.5, +0.0}, {+0.0, +1.0}},
+               {{-0.5, -0.5, +0.0}, {+0.0, +0.0}},
        },
+       .count = 6,
+       .free_data = false,
 };
 
-static VertexLayout sun_vertex_layout = {
-       .attributes = sun_vertex_attributes,
-       .count = 2,
-       .size = sizeof(VertexSun),
-};
-
-static VertexSun sun_vertices[6] = {
-       {{-0.5, -0.5, +0.0}, {+0.0, +0.0}},
-       {{+0.5, -0.5, +0.0}, {+1.0, +0.0}},
-       {{+0.5, +0.5, +0.0}, {+1.0, +1.0}},
-       {{+0.5, +0.5, +0.0}, {+1.0, +1.0}},
-       {{-0.5, +0.5, +0.0}, {+0.0, +1.0}},
-       {{-0.5, -0.5, +0.0}, {+0.0, +0.0}},
-};
-
-typedef struct
-{
-       GLfloat x, y, z;
-} __attribute__((packed)) VertexSkyboxPosition;
-
-typedef struct
-{
-       VertexSkyboxPosition position;
-} __attribute__((packed)) VertexSkybox;
-
-static VertexAttribute skybox_vertex_attributes[2] = {
-       // position
-       {
-               .type = GL_FLOAT,
-               .length = 3,
-               .size = sizeof(VertexSkyboxPosition),
+static GLuint skybox_prog;
+static GLint skybox_loc_VP;
+static GLint skybox_loc_daylight;
+static GLuint skybox_texture_day;
+static GLuint skybox_texture_night;
+typedef struct {
+       v3f32 position;
+} __attribute__((packed)) SkyboxVertex;
+static Mesh skybox_mesh = {
+       .layout = &(VertexLayout) {
+               .attributes = (VertexAttribute[]) {
+                       {GL_FLOAT, 3, sizeof(v3f32)}, // position
+               },
+               .count = 1,
+               .size = sizeof(SkyboxVertex),
        },
+       .vao = 0,
+       .vbo = 0,
+       .data = NULL,
+       .count = 36,
+       .free_data = false,
 };
 
-static VertexLayout skybox_vertex_layout = {
-       .attributes = skybox_vertex_attributes,
-       .count = 1,
-       .size = sizeof(VertexSkybox),
-};
-
-static VertexSkybox skybox_vertices[6][6];
-static VertexSkybox clouds_vertices[6][6];
+static GLuint clouds_prog;
+static GLint clouds_loc_VP;
+static GLint clouds_loc_daylight;
+static Mesh clouds_mesh;
 
 bool sky_init()
 {
-       // skybox
-
-       if (! shader_program_create(RESSOURCE_PATH "shaders/sky/skybox", &sky.skybox_prog, NULL)) {
-               fprintf(stderr, "Failed to create skybox shader program\n");
-               return false;
-       }
-
-       sky.skybox_loc_VP = glGetUniformLocation(sky.skybox_prog, "VP");
-       sky.skybox_loc_daylight = glGetUniformLocation(sky.skybox_prog, "daylight");
-
-       sky.skybox_textures[0] = texture_create_cubemap(RESSOURCE_PATH "textures/skybox/day");
-       sky.skybox_textures[1] = texture_create_cubemap(RESSOURCE_PATH "textures/skybox/night");
-
-       GLint texture_indices[2];
-       for (GLint i = 0; i < 2; i++)
-               texture_indices[i] = i;
-
-       glProgramUniform1iv(sky.skybox_prog, glGetUniformLocation(sky.skybox_prog, "textures"), 2, texture_indices);
-
+       clouds_mesh = skybox_mesh;
+       SkyboxVertex skybox_vertices[6][6], clouds_vertices[6][6];
        for (int f = 0; f < 6; f++) {
                for (int v = 0; v < 6; v++) {
-                       skybox_vertices[f][v].position.x = cube_vertices[f][v].position.x;
-                       skybox_vertices[f][v].position.y = cube_vertices[f][v].position.y;
-                       skybox_vertices[f][v].position.z = cube_vertices[f][v].position.z;
+                       skybox_vertices[f][v].position =
+                               clouds_vertices[f][v].position =
+                               cube_vertices[f][v].position;
+
+                       clouds_vertices[f][v].position.y = fmax(clouds_vertices[f][v].position.y, 0.0);
                }
        }
 
-       sky.skybox_mesh = mesh_create();
-       sky.skybox_mesh->textures = sky.skybox_textures;
-       sky.skybox_mesh->textures_count = 2;
-       sky.skybox_mesh->free_textures = false;
-       sky.skybox_mesh->vertices = skybox_vertices;
-       sky.skybox_mesh->vertices_count = 36;
-       sky.skybox_mesh->free_vertices = false;
-       sky.skybox_mesh->layout = &skybox_vertex_layout;
-
-       // sun
+       // skybox
 
-       if (! shader_program_create(RESSOURCE_PATH "shaders/sky/sun", &sky.sun_prog, NULL)) {
-               fprintf(stderr, "Failed to create sun shader program\n");
+       if (!shader_program_create(RESSOURCE_PATH "shaders/sky/skybox", &skybox_prog, NULL)) {
+               fprintf(stderr, "[error] failed to create skybox shader program\n");
                return false;
        }
 
-       sky.sun_loc_MVP = glGetUniformLocation(sky.sun_prog, "MVP");
-
-       sky.sun_texture = texture_load(RESSOURCE_PATH "textures/sun.png", false);
+       glProgramUniform1iv(skybox_prog, glGetUniformLocation(skybox_prog, "textures"), 2, (GLint[]) {0, 1});
 
-       sky.sun_mesh = mesh_create();
-       sky.sun_mesh->textures = &sky.sun_texture->id;
-       sky.sun_mesh->textures_count = 1;
-       sky.sun_mesh->free_textures = false;
-       sky.sun_mesh->vertices = sun_vertices;
-       sky.sun_mesh->vertices_count = 6;
-       sky.sun_mesh->free_vertices = false;
-       sky.sun_mesh->layout = &sun_vertex_layout;
+       skybox_loc_VP = glGetUniformLocation(skybox_prog, "VP");
+       skybox_loc_daylight = glGetUniformLocation(skybox_prog, "daylight");
+       skybox_texture_day = texture_load_cubemap(RESSOURCE_PATH "textures/skybox/day")->txo;
+       skybox_texture_night = texture_load_cubemap(RESSOURCE_PATH "textures/skybox/night")->txo;
+       skybox_mesh.data = skybox_vertices;
+       mesh_upload(&skybox_mesh);
 
-       // clouds
+       // sun
 
-       if (! shader_program_create(RESSOURCE_PATH "shaders/sky/clouds", &sky.clouds_prog, NULL)) {
-               fprintf(stderr, "Failed to create clouds shader program\n");
+       if (!shader_program_create(RESSOURCE_PATH "shaders/sky/sun", &sun_prog, NULL)) {
+               fprintf(stderr, "[error] failed to create sun shader program\n");
                return false;
        }
 
-       sky.clouds_loc_VP = glGetUniformLocation(sky.clouds_prog, "VP");
-       sky.clouds_loc_daylight = glGetUniformLocation(sky.clouds_prog, "daylight");
+       sun_loc_MVP = glGetUniformLocation(sun_prog, "MVP");
+       sun_texture = texture_load(RESSOURCE_PATH "textures/sun.png", false)->txo;
 
-       for (int f = 0; f < 6; f++) {
-               for (int v = 0; v < 6; v++) {
-                       clouds_vertices[f][v].position.x = cube_vertices[f][v].position.x;
-                       clouds_vertices[f][v].position.y = fmax(cube_vertices[f][v].position.y, 0.0);
-                       clouds_vertices[f][v].position.z = cube_vertices[f][v].position.z;
-               }
+       // clouds
+
+       if (!shader_program_create(RESSOURCE_PATH "shaders/sky/clouds", &clouds_prog, NULL)) {
+               fprintf(stderr, "[error] failed to create clouds shader program\n");
+               return false;
        }
 
-       sky.clouds_mesh = mesh_create();
-       sky.clouds_mesh->textures = sky.skybox_textures;
-       sky.clouds_mesh->textures_count = 1;
-       sky.clouds_mesh->free_textures = false;
-       sky.clouds_mesh->vertices = clouds_vertices;
-       sky.clouds_mesh->vertices_count = 36;
-       sky.clouds_mesh->free_vertices = false;
-       sky.clouds_mesh->layout = &skybox_vertex_layout;
+       clouds_loc_VP = glGetUniformLocation(clouds_prog, "VP");
+       clouds_loc_daylight = glGetUniformLocation(clouds_prog, "daylight");
+       clouds_mesh.data = clouds_vertices;
+       mesh_upload(&clouds_mesh);
 
        return true;
 }
 
 void sky_deinit()
 {
-       glDeleteProgram(sky.skybox_prog);
-       glDeleteTextures(1, &sky.skybox_textures[0]);
-       glDeleteTextures(1, &sky.skybox_textures[1]);
-       mesh_delete(sky.skybox_mesh);
+       glDeleteProgram(sun_prog);
+       mesh_destroy(&sun_mesh);
 
-       glDeleteProgram(sky.sun_prog);
-       mesh_delete(sky.sun_mesh);
-}
+       glDeleteProgram(skybox_prog);
+       mesh_destroy(&skybox_mesh);
 
+       glDeleteProgram(clouds_prog);
+       mesh_destroy(&clouds_mesh);
+}
 
 void sky_render()
 {
@@ -221,28 +155,32 @@ void sky_render()
        view[3][1] = 0.0f;
        view[3][2] = 0.0f;
 
-       mat4x4 VP;
-       mat4x4_mul(VP, scene.projection, view);
+       mat4x4 vp;
+       mat4x4_mul(vp, window.projection, view);
 
-       mat4x4 MVP;
-       mat4x4_mul(MVP, VP, model);
+       mat4x4 mvp;
+       mat4x4_mul(mvp, vp, model);
 
        glDisable(GL_CULL_FACE);
        glDepthFunc(GL_LEQUAL);
 
-       glUseProgram(sky.skybox_prog);
-       glUniformMatrix4fv(sky.skybox_loc_VP, 1, GL_FALSE, VP[0]);
-       glUniform1f(sky.skybox_loc_daylight, daylight);
-       mesh_render(sky.skybox_mesh);
-
-       glUseProgram(sky.sun_prog);
-       glUniformMatrix4fv(sky.sun_loc_MVP, 1, GL_FALSE, MVP[0]);
-       mesh_render(sky.sun_mesh);
-
-       glUseProgram(sky.clouds_prog);
-       glUniformMatrix4fv(sky.clouds_loc_VP, 1, GL_FALSE, VP[0]);
-       glUniform1f(sky.clouds_loc_daylight, daylight);
-       mesh_render(sky.clouds_mesh);
+       glUseProgram(skybox_prog);
+       glUniformMatrix4fv(skybox_loc_VP, 1, GL_FALSE, vp[0]);
+       glUniform1f(skybox_loc_daylight, daylight);
+       glBindTextureUnit(0, skybox_texture_day);
+       glBindTextureUnit(1, skybox_texture_night);
+       mesh_render(&skybox_mesh);
+
+       glUseProgram(sun_prog);
+       glUniformMatrix4fv(sun_loc_MVP, 1, GL_FALSE, mvp[0]);
+       glBindTextureUnit(0, sun_texture);
+       mesh_render(&sun_mesh);
+
+       glUseProgram(clouds_prog);
+       glUniformMatrix4fv(clouds_loc_VP, 1, GL_FALSE, vp[0]);
+       glUniform1f(clouds_loc_daylight, daylight);
+       glBindTextureUnit(0, skybox_texture_day);
+       mesh_render(&clouds_mesh);
 
        glDepthFunc(GL_LESS);
        glEnable(GL_CULL_FACE);
index 32939317d883c3f8c900bacf19a068cad9c16f7f..027abe714e4e5f3867f1da8ef4ecc468709b9123 100644 (file)
@@ -7,4 +7,4 @@ bool sky_init();
 void sky_deinit();
 void sky_render();
 
-#endif
+#endif // _SKY_H_
diff --git a/src/client/terrain_gfx.c b/src/client/terrain_gfx.c
new file mode 100644 (file)
index 0000000..38c7799
--- /dev/null
@@ -0,0 +1,300 @@
+#include <asprintf/asprintf.h>
+#include <linmath.h/linmath.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "client/camera.h"
+#include "client/client_config.h"
+#include "client/client_node.h"
+#include "client/client_terrain.h"
+#include "client/cube.h"
+#include "client/frustum.h"
+#include "client/shader.h"
+#include "client/terrain_gfx.h"
+#include "day.h"
+
+typedef struct {
+       bool visible;
+       bool transparent;
+       ModelBatch *batch;
+       TerrainChunk *chunk;
+       v3s32 chunkp;
+       TerrainChunk *nbrs[6];
+       bool tried_nbrs[6];
+       bool cull_edges;
+} ChunkRenderData;
+
+static VertexLayout terrain_vertex_layout = {
+       .attributes = (VertexAttribute []) {
+               {GL_FLOAT, 3, sizeof(v3f32)}, // position
+               {GL_FLOAT, 3, sizeof(v3f32)}, // normal
+               {GL_FLOAT, 2, sizeof(v2f32)}, // textureCoordinates
+               {GL_FLOAT, 1, sizeof(f32  )}, // textureIndex
+               {GL_FLOAT, 3, sizeof(v3f32)}, // color
+       },
+       .count = 5,
+       .size = sizeof(TerrainVertex),
+};
+
+static v3s32 face_dir[6] = {
+       {+0, +0, -1},
+       {+0, +0, +1},
+       {-1, +0, +0},
+       {+1, +0, +0},
+       {+0, -1, +0},
+       {+0, +1, +0},
+};
+
+static v3f32 center_offset = {
+       CHUNK_SIZE * 0.5f + 0.5f,
+       CHUNK_SIZE * 0.5f + 0.5f,
+       CHUNK_SIZE * 0.5f + 0.5f,
+};
+
+static GLuint shader_prog;
+static GLint loc_model;
+static GLint loc_VP;
+static GLint loc_daylight;
+static GLint loc_fogColor;
+static GLint loc_ambientLight;
+static GLint loc_lightDir;
+static GLint loc_cameraPos;
+
+static ModelShader model_shader;
+
+static inline bool cull_face(NodeType self, NodeType nbr)
+{
+       switch (client_node_definitions[self].visibility) {
+               case VISIBILITY_CLIP:
+                       return false;
+
+               case VISIBILITY_BLEND:
+                       return self == nbr;
+
+               case VISIBILITY_SOLID:
+                       return nbr == NODE_UNLOADED
+                               || client_node_definitions[nbr].visibility == VISIBILITY_SOLID;
+
+               default: // impossible
+                       break;
+       }
+
+       // impossible
+       return false;
+}
+
+static inline void render_node(ChunkRenderData *data, v3s32 offset)
+{
+       NodeArgsRender args;
+
+       args.node = &data->chunk->data[offset.x][offset.y][offset.z];
+
+       ClientNodeDefinition *def = &client_node_definitions[args.node->type];
+       if (def->visibility == VISIBILITY_NONE)
+               return;
+
+       v3f32 vertex_offset = v3f32_sub(v3s32_to_f32(offset), center_offset);
+       if (def->render)
+               args.pos = v3s32_add(offset, data->chunkp);
+
+       for (args.f = 0; args.f < 6; args.f++) {
+               v3s32 nbr_offset = v3s32_add(offset, face_dir[args.f]);
+
+               TerrainChunk *nbr_chunk;
+
+               if (nbr_offset.z >= 0 && nbr_offset.z < CHUNK_SIZE &&
+                       nbr_offset.x >= 0 && nbr_offset.x < CHUNK_SIZE &&
+                       nbr_offset.y >= 0 && nbr_offset.y < CHUNK_SIZE) {
+                       nbr_chunk = data->chunk;
+               } else if (!(nbr_chunk = data->nbrs[args.f]) && !data->tried_nbrs[args.f]) {
+                       nbr_chunk = data->nbrs[args.f] = terrain_get_chunk(client_terrain,
+                               v3s32_add(data->chunk->pos, face_dir[args.f]), false);
+                       data->tried_nbrs[args.f] = true;
+               }
+
+               NodeType nbr_node = NODE_UNLOADED;
+               if (nbr_chunk)
+                       nbr_node = nbr_chunk->data
+                               [(nbr_offset.x + CHUNK_SIZE) % CHUNK_SIZE]
+                               [(nbr_offset.y + CHUNK_SIZE) % CHUNK_SIZE]
+                               [(nbr_offset.z + CHUNK_SIZE) % CHUNK_SIZE].type;
+
+               if (cull_face(args.node->type, nbr_node)) {
+                       // exception to culling rules: don't cull solid edge nodes, unless cull_edges
+                       if (data->cull_edges || nbr_chunk == data->chunk || def->visibility != VISIBILITY_SOLID)
+                               continue;
+               } else {
+                       data->visible = true;
+               }
+
+               if (def->visibility == VISIBILITY_BLEND)
+                       data->transparent = true;
+
+               for (args.v = 0; args.v < 6; args.v++) {
+                       args.vertex.cube = cube_vertices[args.f][args.v];
+                       args.vertex.cube.position = v3f32_add(args.vertex.cube.position, vertex_offset);
+                       args.vertex.color = (v3f32) {1.0f, 1.0f, 1.0f};
+
+                       if (def->render)
+                               def->render(&args);
+
+                       model_batch_add_vertex(data->batch, def->tiles.textures[args.f]->txo, &args.vertex);
+               }
+       }
+}
+
+static void animate_chunk_model(Model *model, f64 dtime)
+{
+       if ((model->root->scale.x += dtime * 2.0f) > 1.0f) {
+               model->root->scale.x = 1.0f;
+               client_terrain_meshgen_task(model->extra);
+       }
+
+       model->root->scale.z
+               = model->root->scale.y
+               = model->root->scale.x;
+
+       model_node_transform(model->root);
+}
+
+static Model *create_chunk_model(TerrainChunk *chunk, bool animate)
+{
+       ChunkRenderData data = {
+               .visible = false,
+               .transparent = false,
+               .batch = model_batch_create(&model_shader, &terrain_vertex_layout, offsetof(TerrainVertex, textureIndex)),
+               .chunk = chunk,
+               .chunkp = v3s32_scale(chunk->pos, CHUNK_SIZE),
+               .nbrs = {NULL},
+               .tried_nbrs = {false},
+               .cull_edges = !animate,
+       };
+
+       CHUNK_ITERATE
+               render_node(&data, (v3s32) {x, y, z});
+
+       if (!data.batch->textures.siz) {
+               model_batch_free(data.batch);
+               return NULL;
+       }
+
+       Model *model = model_create();
+       model->extra = chunk;
+       model->box = (aabb3f32) {
+               v3f32_sub((v3f32) {-1.0f, -1.0f, -1.0f}, center_offset),
+               v3f32_add((v3f32) {+1.0f, +1.0f, +1.0f}, center_offset)};
+       model->callbacks.step = animate ? &animate_chunk_model : NULL;
+       model->callbacks.delete = &model_free_meshes;
+       model->flags.frustum_culling = 1;
+       model->flags.transparent = data.transparent;
+
+       model->root->visible = data.visible;
+       model->root->pos = v3f32_add(v3s32_to_f32(data.chunkp), center_offset);
+       model->root->scale = (v3f32) {0.0f, 0.0f, 0.0f};
+
+       if (data.visible)
+               model_node_add_batch(model->root, data.batch);
+       else
+               model_batch_free(data.batch);
+
+       return model;
+}
+
+bool terrain_gfx_init()
+{
+       GLint texture_units;
+       glGetIntegerv(GL_MAX_TEXTURE_UNITS, &texture_units);
+
+       char *shader_defs;
+       asprintf(&shader_defs,
+               "#define MAX_TEXTURE_UNITS %d\n"
+               "#define VIEW_DISTANCE %lf\n",
+               texture_units,
+               client_config.view_distance
+       );
+
+       if (!shader_program_create(RESSOURCE_PATH "shaders/terrain", &shader_prog, shader_defs)) {
+               fprintf(stderr, "[error] failed to create terrain shader program\n");
+               return false;
+       }
+
+       free(shader_defs);
+
+       loc_model = glGetUniformLocation(shader_prog, "model");
+       loc_VP = glGetUniformLocation(shader_prog, "VP");
+       loc_daylight = glGetUniformLocation(shader_prog, "daylight");
+       loc_fogColor = glGetUniformLocation(shader_prog, "fogColor");
+       loc_ambientLight = glGetUniformLocation(shader_prog, "ambientLight");
+       loc_lightDir = glGetUniformLocation(shader_prog, "lightDir");
+       loc_cameraPos = glGetUniformLocation(shader_prog, "cameraPos");
+
+       GLint texture_indices[texture_units];
+       for (GLint i = 0; i < texture_units; i++)
+               texture_indices[i] = i;
+
+       glProgramUniform1iv(shader_prog, glGetUniformLocation(shader_prog, "textures"), texture_units, texture_indices);
+
+       model_shader.prog = shader_prog;
+       model_shader.loc_transform = loc_model;
+
+       return true;
+}
+
+void terrain_gfx_deinit()
+{
+       glDeleteProgram(shader_prog);
+}
+
+void terrain_gfx_update()
+{
+       vec4 base_sunlight_dir = {0.0f, 0.0f, -1.0f, 1.0f};
+       vec4 sunlight_dir;
+       mat4x4 sunlight_mat;
+       mat4x4_identity(sunlight_mat);
+
+       mat4x4_rotate(sunlight_mat, sunlight_mat, 1.0f, 0.0f, 0.0f, get_sun_angle() + M_PI / 2.0f);
+       mat4x4_mul_vec4(sunlight_dir, sunlight_mat, base_sunlight_dir);
+
+       f32 daylight = get_daylight();
+       f32 ambient_light = f32_mix(0.3f, 0.7f, daylight);
+       v3f32 fog_color = v3f32_mix((v3f32) {0x03, 0x0A, 0x1A}, (v3f32) {0x87, 0xCE, 0xEB}, daylight);
+
+       glProgramUniformMatrix4fv(shader_prog, loc_VP, 1, GL_FALSE, frustum[0]);
+       glProgramUniform3f(shader_prog, loc_lightDir, sunlight_dir[0], sunlight_dir[1], sunlight_dir[2]);
+       glProgramUniform3f(shader_prog, loc_cameraPos, camera.eye[0], camera.eye[1], camera.eye[2]);
+       glProgramUniform1f(shader_prog, loc_daylight, daylight);
+       glProgramUniform3f(shader_prog, loc_fogColor, fog_color.x / 0xFF * ambient_light, fog_color.y / 0xFF * ambient_light, fog_color.z / 0xFF * ambient_light);
+       glProgramUniform1f(shader_prog, loc_ambientLight, ambient_light);
+}
+
+void terrain_gfx_make_chunk_model(TerrainChunk *chunk)
+{
+       TerrainChunkMeta *meta = chunk->extra;
+
+       bool animate = true;
+
+       pthread_mutex_lock(&chunk->mtx);
+       if (meta->model && meta->model->root->scale.x == 1.0f)
+               animate = false;
+       pthread_mutex_unlock(&chunk->mtx);
+
+       Model *model = create_chunk_model(chunk, animate);
+
+       pthread_mutex_lock(&chunk->mtx);
+
+       if (meta->model) {
+               if (model) {
+                       model->callbacks.step = meta->model->callbacks.step;
+                       model->root->scale = meta->model->root->scale;
+                       model_node_transform(model->root);
+               }
+
+               meta->model->flags.delete = 1;
+       }
+
+       meta->model = model;
+
+       if (model)
+               model_scene_add(model);
+
+       pthread_mutex_unlock(&chunk->mtx);
+}
diff --git a/src/client/terrain_gfx.h b/src/client/terrain_gfx.h
new file mode 100644 (file)
index 0000000..d41bce8
--- /dev/null
@@ -0,0 +1,18 @@
+#ifndef _TERRAIN_GFX_H_
+#define _TERRAIN_GFX_H_
+
+#include "client/cube.h"
+#include "terrain.h"
+
+typedef struct {
+       CubeVertex cube;
+       f32 textureIndex;
+       v3f32 color;
+} __attribute__((packed)) TerrainVertex;
+
+bool terrain_gfx_init();
+void terrain_gfx_deinit();
+void terrain_gfx_update();
+void terrain_gfx_make_chunk_model(TerrainChunk *chunk);
+
+#endif
index 1036a91218f133e457f56d63116118fd6551a43a..9f4f7ec74c9ce4e0fdb691de4c19c3c8d738d053 100644 (file)
@@ -1,57 +1,83 @@
 #define STB_IMAGE_IMPLEMENTATION
 #include <stb/stb_image.h>
 #include <stdbool.h>
-#include <dragonstd/list.h>
+#include <dragonstd/tree.h>
 #include "client/client_config.h"
 #include "client/texture.h"
-#include "util.h"
 
-static List textures;
+static Tree textures;
 
-__attribute((constructor(101))) static void textures_init()
+typedef struct {
+       char *path;
+       Texture texture;
+} TextureLookup;
+
+static int cmp_texture(TextureLookup *texture, char *path)
 {
-       textures = list_create(&list_compare_string);
+       return strcmp(texture->path, path);
 }
 
-static void list_delete_texture(unused void *key, void *value, unused void *arg)
+static bool lookup_texture(char *path, Texture **texture)
 {
-       texture_delete(value);
+       TreeNode **node = tree_nfd(&textures, path, &cmp_texture);
+
+       if (*node) {
+               *texture = &((TextureLookup *) &(*node)->dat)->texture;
+               return true;
+       }
+
+       TextureLookup *lookup = malloc(sizeof *lookup);
+       lookup->path = strdup(path);
+       *texture = &lookup->texture;
+
+       tree_nmk(&textures, node, lookup);
+       return false;
 }
 
-__attribute((destructor)) static void textures_deinit()
+static void delete_texture(TextureLookup *lookup)
 {
-       list_clear_func(&textures, &list_delete_texture, NULL);
+       free(lookup->path);
+       texture_destroy(&lookup->texture);
+       free(lookup);
 }
 
-Texture *texture_create(unsigned char *data, int width, int height, GLenum format, bool mipmap)
+__attribute__((constructor(101))) static void textures_init()
 {
-       Texture *texture = malloc(sizeof(Texture));
-       texture->width = width;
-       texture->height = height;
-
-       glGenTextures(1, &texture->id);
-
-       glBindTexture(GL_TEXTURE_2D, texture->id);
+       tree_ini(&textures);
+}
 
-       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (mipmap && client_config.mipmap) ? GL_NEAREST_MIPMAP_NEAREST : GL_NEAREST);
-       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
-       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
-       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+__attribute__((destructor)) static void textures_deinit()
+{
+       tree_clr(&textures, &delete_texture, NULL, NULL, 0);
+}
 
-       glTexImage2D(GL_TEXTURE_2D, 0, format, texture->width, texture->height, 0, format, GL_UNSIGNED_BYTE, data);
-       glGenerateMipmap(GL_TEXTURE_2D);
+Texture *texture_load(char *path, bool mipmap)
+{
+       Texture *texture;
+       if (lookup_texture(path, &texture))
+               return texture;
+
+       unsigned char *data = stbi_load(path,
+               &texture->width, &texture->height, &texture->channels, 0);
+       if (!data) {
+               fprintf(stderr, "[error] failed to load texture %s\n", path);
+               exit(EXIT_FAILURE);
+       }
 
-       glBindTexture(GL_TEXTURE_2D, 0);
+       texture_upload(texture, data, GL_RGBA, mipmap);
+       stbi_image_free(data);
 
        return texture;
 }
 
-GLuint texture_create_cubemap(char *path)
+Texture *texture_load_cubemap(char *path)
 {
-       GLuint id;
-       glGenTextures(1, &id);
+       Texture *texture;
+       if (lookup_texture(path, &texture))
+               return texture;
 
-       glBindTexture(GL_TEXTURE_CUBE_MAP, id);
+       glGenTextures(1, &texture->txo);
+       glBindTexture(GL_TEXTURE_CUBE_MAP, texture->txo);
 
        const char *directions[6] = {
                "right",
@@ -66,14 +92,15 @@ GLuint texture_create_cubemap(char *path)
                char filename[strlen(path) + 1 + strlen(directions[i]) + 1 + 3 + 1];
                sprintf(filename, "%s/%s.png", path, directions[i]);
 
-               int width, height, channels;
-               unsigned char *data = stbi_load(filename, &width, &height, &channels, 0);
-               if (! data) {
-                       fprintf(stderr, "Failed to load texture %s\n", filename);
-                       return 0;
+               unsigned char *data = stbi_load(filename,
+                       &texture->width, &texture->height, &texture->channels, 0);
+               if (!data) {
+                       fprintf(stderr, "[error] failed to load texture %s\n", filename);
+                       exit(EXIT_FAILURE);
                }
 
-               glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
+               glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGBA,
+                       texture->width, texture->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
                stbi_image_free(data);
        }
 
@@ -83,30 +110,26 @@ GLuint texture_create_cubemap(char *path)
        glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
 
-       return id;
+       return texture;
 }
 
-void texture_delete(Texture *texture)
+void texture_upload(Texture *texture, unsigned char *data, GLenum format, bool mipmap)
 {
-       glDeleteTextures(1, &texture->id);
-       free(texture);
-}
-
-Texture *texture_load(char *path, bool mipmap)
-{
-       int width, height, channels;
-
-       unsigned char *data = stbi_load(path, &width, &height, &channels, 0);
-       if (! data) {
-               fprintf(stderr, "Failed to load texture %s\n", path);
-               return NULL;
-       }
-
-       Texture *texture = texture_create(data, width, height, GL_RGBA, mipmap);
+       glGenTextures(1, &texture->txo);
+       glBindTexture(GL_TEXTURE_2D, texture->txo);
 
-       stbi_image_free(data);
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (mipmap && client_config.mipmap)
+               ? GL_NEAREST_MIPMAP_NEAREST : GL_NEAREST);
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
 
-       list_put(&textures, texture, NULL);
+       glTexImage2D(GL_TEXTURE_2D, 0, format,
+               texture->width, texture->height, 0, format, GL_UNSIGNED_BYTE, data);
+       glGenerateMipmap(GL_TEXTURE_2D);
+}
 
-       return texture;
+void texture_destroy(Texture *texture)
+{
+       glDeleteTextures(1, &texture->txo);
 }
index 77375752ffef545265d95e3194a20f41ce83c019..933fe72b9cbc5c72ca6925e12078ee644c0c43eb 100644 (file)
@@ -4,15 +4,14 @@
 #include <GL/glew.h>
 #include <GL/gl.h>
 
-typedef struct
-{
-       GLuint id;
-       int width, height;
+typedef struct {
+       GLuint txo;
+       int width, height, channels;
 } Texture;
 
-Texture *texture_create(unsigned char *data, int width, int height, GLenum format, bool mipmap);
-GLuint texture_create_cubemap(char *path);
-void texture_delete(Texture *texture);
 Texture *texture_load(char *path, bool mipmap);
+Texture *texture_load_cubemap(char *path);
+void texture_upload(Texture *texture, unsigned char *data, GLenum format, bool mipmap);
+void texture_destroy(Texture *texture);
 
-#endif
+#endif // _TEXTURE_H_
diff --git a/src/client/vertex.c b/src/client/vertex.c
deleted file mode 100644 (file)
index d203668..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-#include "client/vertex.h"
-
-void vertex_layout_configure(VertexLayout *layout)
-{
-       size_t offset = 0;
-
-       for (GLuint i = 0; i < layout->count; i++) {
-               VertexAttribute *attrib = &layout->attributes[i];
-
-               glVertexAttribPointer(i, attrib->length, attrib->type, GL_FALSE, layout->size, (GLvoid *) offset);
-               glEnableVertexAttribArray(i);
-
-               offset += attrib->size;
-       }
-}
diff --git a/src/client/vertex.h b/src/client/vertex.h
deleted file mode 100644 (file)
index 361d5f6..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-#ifndef _VERTEX_H_
-#define _VERTEX_H_
-
-#include <GL/glew.h>
-#include <GL/gl.h>
-
-typedef struct
-{
-       GLenum type;
-       GLsizei length;
-       GLsizei size;
-} VertexAttribute;
-
-typedef struct
-{
-       VertexAttribute *attributes;
-       GLuint count;
-       GLsizei size;
-} VertexLayout;
-
-void vertex_layout_configure(VertexLayout *layout);
-
-#endif
index b45b95bf8ec268777a8cf76ac5f67c662305cfc6..63dc81f01f812c8d1df1402a794bd3f96558883f 100644 (file)
@@ -6,36 +6,45 @@
 #include "client/game.h"
 #include "client/gui.h"
 #include "client/input.h"
-#include "client/scene.h"
 #include "client/window.h"
-#include "util.h"
 
 struct Window window;
 
-static void framebuffer_size_callback(unused GLFWwindow *handle, int width, int height)
+static int small_x, small_y, small_width, small_height;
+
+static void update_projection()
+{
+       mat4x4_perspective(window.projection,
+               window.fov / 180.0f * M_PI,
+               (float) window.width / (float) window.height,
+               0.01f, client_config.view_distance + 28.0f);
+}
+
+static void framebuffer_size_callback(__attribute__((unused)) GLFWwindow *handle, int width, int height)
 {
        glViewport(0, 0, width, height);
+       window.width = width;
+       window.height = height;
 
-       if (! window.fullscreen) {
-               window.small_bounds.width = width;
-               window.small_bounds.height = height;
+       if (!window.fullscreen) {
+               small_width = width;
+               small_height = height;
        }
 
-       scene_on_resize(width, height);
-       gui_on_resize(width, height);
-       game_on_resize(width, height);
+       update_projection();
+       gui_update_projection();
 }
 
-static void cursor_pos_callback(unused GLFWwindow *handle, double current_x, double current_y)
+static void cursor_pos_callback(__attribute__((unused)) GLFWwindow *handle, double x, double y)
 {
-       input_on_cursor_pos(current_x, current_y);
+       input_cursor(x, y);
 }
 
-static void window_pos_callback(unused GLFWwindow *handle, int x, int y)
+static void window_pos_callback(__attribute__((unused)) GLFWwindow *handle, int x, int y)
 {
-       if (! window.fullscreen) {
-               window.small_bounds.x = x;
-               window.small_bounds.y = y;
+       if (!window.fullscreen) {
+               small_x = x;
+               small_y = y;
        }
 }
 
@@ -46,21 +55,21 @@ void window_enter_fullscreen()
        const GLFWvidmode *vidmode = glfwGetVideoMode(monitor);
        glfwSetWindowMonitor(window.handle, monitor, 0, 0, vidmode->width, vidmode->height, 0);
 
-       debug_menu_update_fullscreen();
+       debug_menu_changed(ENTRY_FULLSCREEN);
 }
 
 void window_exit_fullscreen()
 {
        window.fullscreen = false;
-       glfwSetWindowMonitor(window.handle, NULL, window.small_bounds.x, window.small_bounds.y, window.small_bounds.width, window.small_bounds.height, 0);
+       glfwSetWindowMonitor(window.handle, NULL, small_x, small_y, small_width, small_height, 0);
 
-       debug_menu_update_fullscreen();
+       debug_menu_changed(ENTRY_FULLSCREEN);
 }
 
-bool window_init(int width, int height)
+bool window_init()
 {
-       if(! glfwInit()) {
-               fprintf(stderr, "Failed to initialize GLFW\n");
+       if(!glfwInit()) {
+               fprintf(stderr, "[error] failed to initialize GLFW\n");
                return false;
        }
 
@@ -69,24 +78,29 @@ bool window_init(int width, int height)
        glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
        glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
 
-       window.handle = glfwCreateWindow(width, height, "Dragonblocks", NULL, NULL);
+       window.width = 1250;
+       window.height = 750;
+       window.handle = glfwCreateWindow(window.width, window.height, "Dragonblocks", NULL, NULL);
+       window.fullscreen = false;
+       window.fov = 86.1f;
+       update_projection();
 
-       window.small_bounds.width = width;
-       window.small_bounds.height = height;
+       small_width = window.width;
+       small_height = window.height;
 
-       if (! window.handle) {
-               fprintf(stderr, "Failed to create window\n");
+       if (!window.handle) {
+               fprintf(stderr, "[error] failed to create window\n");
                glfwTerminate();
                return false;
        }
 
        glfwMakeContextCurrent(window.handle);
 
-       if (! client_config.vsync)
+       if (!client_config.vsync)
                glfwSwapInterval(0);
 
        if (glewInit() != GLEW_OK) {
-               fprintf(stderr, "Failed to initialize GLEW\n");
+               fprintf(stderr, "[error] failed to initialize GLEW\n");
                return false;
        }
 
index 83b784f70ab2f3b6e354deafd248544ebc5900d9..0203510a4d6d64e0709c14e1602106c9d8187909 100644 (file)
@@ -3,19 +3,16 @@
 
 #include <GLFW/glfw3.h>
 
-extern struct Window
-{
+extern struct Window {
+       int width, height;
        GLFWwindow *handle;
        bool fullscreen;
-       struct
-       {
-               int x, y;
-               int width, height;
-       } small_bounds;
+       f32 fov;
+       mat4x4 projection;
 } window;
 
-bool window_init(int width, int height);
+bool window_init();
 void window_enter_fullscreen();
 void window_exit_fullscreen();
 
-#endif
+#endif // _WINDOW_H_
diff --git a/src/color.c b/src/color.c
new file mode 100644 (file)
index 0000000..66c5d84
--- /dev/null
@@ -0,0 +1,40 @@
+#include "color.h"
+
+static f32 hue_to_rgb(f32 p, f32 q, f32 t)
+{
+    if (t < 0.0f)
+        t += 1.0f;
+
+    if (t > 1.0f)
+        t -= 1.0f;
+
+    if (t < 1.0f / 6.0f)
+        return p + (q - p) * 6.0f * t;
+
+    if (t < 1.0f / 2.0f)
+        return q;
+
+    if (t < 2.0f / 3.0f)
+        return p + (q - p) * (2.0f / 3.0f - t) * 6.0f;
+
+    return p;
+}
+
+v3f32 hsl_to_rgb(v3f32 hsl)
+{
+       v3f32 rgb;
+
+    if (hsl.y == 0.0f) {
+               rgb = (v3f32) {hsl.z, hsl.z, hsl.z};
+    } else {
+        f32 q = hsl.z < 0.5f ? hsl.z * (1.0f + hsl.y) : hsl.z + hsl.y - hsl.z * hsl.y;
+        f32 p = 2.0f * hsl.z - q;
+
+        rgb.x = hue_to_rgb(p, q, hsl.x + 1.0f / 3.0f);
+        rgb.y = hue_to_rgb(p, q, hsl.x);
+        rgb.z = hue_to_rgb(p, q, hsl.x - 1.0f / 3.0f);
+    }
+
+    return rgb;
+}
+
diff --git a/src/color.h b/src/color.h
new file mode 100644 (file)
index 0000000..ebf8d73
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef _COLOR_H_
+#define _COLOR_H_
+
+#include "types.h"
+
+v3f32 hsl_to_rgb(v3f32 hsl);
+
+#endif // _COLOR_H_
index 00c50b584931e233f58257180d7adfc2e4b96eda..8f200c8ab2e78a68d2765ba7a1de11263b93a3e3 100644 (file)
@@ -8,14 +8,14 @@ void config_read(char *path, ConfigEntry *entries, size_t num_entries)
 {
        FILE *f = fopen(path, "r");
 
-       if (! f)
+       if (!f)
                return;
 
-       printf("Reading config from %s\n", path);
+       printf("[info] reading config from %s\n", path);
 
-       while (! feof(f)) {
+       while (!feof(f)) {
                char key[BUFSIZ];
-               if (! fscanf(f, "%s", key))
+               if (!fscanf(f, "%s", key))
                        break;
 
                bool found = false;
@@ -25,38 +25,45 @@ void config_read(char *path, ConfigEntry *entries, size_t num_entries)
                                bool valid = false;
 
                                switch (entry->type) {
-                                       case CT_STRING: {
+                                       case CONFIG_STRING: {
                                                char value[BUFSIZ];
 
-                                               if (! fscanf(f, "%s", value))
+                                               if (!fscanf(f, "%s", value))
                                                        break;
 
                                                valid = true;
-                                               *(char **) entry->value = strdup(value);
-                                       } break;
 
-                                       case CT_INT:
+                                               char **entry_value = entry->value;
+
+                                               if (*entry_value)
+                                                       free(*entry_value);
+                                               *entry_value = strdup(value);
+
+                                               break;
+                                       }
+
+                                       case CONFIG_INT:
                                                if (fscanf(f, "%d", (int *) entry->value))
                                                        valid = true;
 
                                                break;
 
-                                       case CT_UINT:
+                                       case CONFIG_UINT:
                                                if (fscanf(f, "%u", (unsigned int *) entry->value))
                                                        valid = true;
 
                                                break;
 
-                                       case CT_FLOAT:
+                                       case CONFIG_FLOAT:
                                                if (fscanf(f, "%lf", (double *) entry->value))
                                                        valid = true;
 
                                                break;
 
-                                       case CT_BOOL: {
+                                       case CONFIG_BOOL: {
                                                char value[BUFSIZ];
 
-                                               if (! fscanf(f, "%s", value))
+                                               if (!fscanf(f, "%s", value))
                                                        break;
 
                                                valid = true;
@@ -68,20 +75,35 @@ void config_read(char *path, ConfigEntry *entries, size_t num_entries)
                                                else
                                                        valid = false;
 
-                                       } break;
+                                               break;
+                                       }
                                }
 
-                               if (! valid)
-                                       fprintf(stderr, "Invalid value for setting %s in %s\n", key, path);
+                               if (!valid)
+                                       fprintf(stderr, "[warning] invalid value for setting %s in %s\n", key, path);
 
                                found = true;
                                break;
                        }
                }
 
-               if (! found)
-                       fprintf(stderr, "Unknown setting %s in %s\n", key, path);
+               if (!found)
+                       fprintf(stderr, "[warning] unknown setting %s in %s\n", key, path);
        }
 
        fclose(f);
 }
+
+void config_free(ConfigEntry *entries, size_t num_entries)
+{
+       for (size_t i = 0; i < num_entries; i++) {
+               ConfigEntry *entry = &entries[i];
+
+               if (entry->type == CONFIG_STRING) {
+                       char **entry_value = entry->value;
+
+                       if (*entry_value)
+                               free(*entry_value);
+               }
+       }
+}
index c69a5fdb559cb5323df26084f6e6412ff60feab6..c7eeab3ce6ff62a6abb561ed37d20fd8a3203fe6 100644 (file)
@@ -3,22 +3,21 @@
 
 #include <stddef.h>
 
-typedef enum
-{
-       CT_STRING,
-       CT_INT,
-       CT_UINT,
-       CT_FLOAT,
-       CT_BOOL,
+typedef enum {
+       CONFIG_STRING,
+       CONFIG_INT,
+       CONFIG_UINT,
+       CONFIG_FLOAT,
+       CONFIG_BOOL,
 } ConfigType;
 
-typedef struct
-{
+typedef struct {
        ConfigType type;
        char *key;
        void *value;
 } ConfigEntry;
 
 void config_read(char *path, ConfigEntry *entries, size_t num_entries);
+void config_free(ConfigEntry *entries, size_t num_entires);
 
-#endif
+#endif // _CONFIG_H_
index 24b6b4b4117d3995d759ae13f6686b712b78afd6..e28e21b87bec6027ad74a688acbf3ff23edec5fb 100644 (file)
--- a/src/day.c
+++ b/src/day.c
@@ -1,7 +1,6 @@
 #include <math.h>
 #include <time.h>
 #include "day.h"
-#include "util.h"
 
 bool timelapse = false;
 static f64 time_of_day_offset;
index b35e12efc052744a4290af2352cd062be180d727..75cf3e906f8749fe8336a737ad4ed2d776ccdb76 100644 (file)
--- a/src/day.h
+++ b/src/day.h
@@ -3,6 +3,7 @@
 
 #include <stdbool.h>
 #include "types.h"
+
 #define MINUTES_PER_HOUR 60
 #define HOURS_PER_DAY 24
 #define MINUTES_PER_DAY (HOURS_PER_DAY * MINUTES_PER_HOUR)
@@ -17,4 +18,4 @@ void split_time_of_day(int *hours, int *minutes);
 
 extern bool timelapse;
 
-#endif
+#endif // _DAY_H_
index 98d38c4abc20803c924ba6d7c6d2197260cb05f5..7f80044999a6238f3b82eae2cd94e6a87e54db76 100755 (executable)
@@ -1,4 +1,5 @@
-#! /bin/bash
+#!/bin/bash
+
 if ! make -j$(nproc); then
        exit 1
 fi
@@ -29,13 +30,13 @@ set print thread-events off
 run \"[::1]:4000\"
 " > $DEBUG_DIR/server_script
 
-kitty --detach -e bash -c "
+alacritty -e bash -c "
        echo \$\$ > $DEBUG_DIR/server_pid
-       exec gdb --command $DEBUG_DIR/server_script ./DragonblocksServer
-"
+       exec gdb --command $DEBUG_DIR/server_script ./dragonblocks_server
+" &
 sleep 0.5
 
-gdb --command $DEBUG_DIR/client_script ./Dragonblocks
+gdb --command $DEBUG_DIR/client_script ./dragonblocks
 
 kill `cat $DEBUG_DIR/server_pid`
 
index 199a3bd504555dd8fced459789e97927f8b60729..3c6c4606edcdcf1ee2e98028b143b741a16eb4f7 100755 (executable)
@@ -1,8 +1,8 @@
-#! /bin/bash
+#!/bin/bash
 COMMAND="./debug.sh"
 
-if [[ $1 == "mapgen" ]]; then
-       COMMAND="./debug_mapgen.sh"
+if [[ $1 == "terrain" ]]; then
+       COMMAND="./debug_terrain.sh"
 fi
 
 while true; do
diff --git a/src/debug_mapgen.sh b/src/debug_mapgen.sh
deleted file mode 100755 (executable)
index c216dca..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-#! /bin/bash
-rm -f map.sqlite
-./debug.sh
diff --git a/src/debug_terrain.sh b/src/debug_terrain.sh
new file mode 100755 (executable)
index 0000000..7da1b80
--- /dev/null
@@ -0,0 +1,3 @@
+#!/bin/bash
+rm -f *.sqlite
+./debug.sh
diff --git a/src/entity.h b/src/entity.h
new file mode 100644 (file)
index 0000000..f564cee
--- /dev/null
@@ -0,0 +1,11 @@
+#ifndef _ENTITY_H_
+#define _ENTITY_H_
+
+// ET phone home
+typedef enum {
+       ENTITY_LOCALPLAYER,
+       ENTITY_PLAYER,
+       COUNT_ENTITY,
+} EntityType;
+
+#endif // _ENTITY_H_
index 02db27617be48e0fe22054f128ea2ca733488d54..5341603e8bbb74a019d10fb5d9f3ad7651de53f5 100644 (file)
@@ -1,6 +1,5 @@
 #include "environment.h"
 #include "perlin.h"
-#include "util.h"
 
 f64 get_humidity(v3s32 pos)
 {
index 1de120c775092f5d3dfeac38a119ca0ea06eea3b..f9a3ef785601c2cabac1e781fe16c7ea75a8ccef 100644 (file)
@@ -6,4 +6,4 @@
 f64 get_humidity(v3s32 pos);
 f64 get_temperature(v3s32 pos);
 
-#endif
+#endif // _ENVIRONMENT_H_
index 2fb1d0181cc309690a886744778e90222e492af4..c11c3cda4f151832cba17342725e3b123b24a9a7 100644 (file)
@@ -3,18 +3,18 @@
 #include <string.h>
 #include "interrupt.h"
 
-Flag *interrupt;
+Flag interrupt;
 static struct sigaction sa = {0};
 
 static void interrupt_handler(int sig)
 {
        fprintf(stderr, "%s\n", strsignal(sig));
-       flag_set(interrupt);
+       flag_set(&interrupt);
 }
 
 void interrupt_init()
 {
-       interrupt = flag_create();
+       flag_ini(&interrupt);
 
        sa.sa_handler = &interrupt_handler;
        sigaction(SIGINT, &sa, NULL);
@@ -23,5 +23,5 @@ void interrupt_init()
 
 void interrupt_deinit()
 {
-       flag_delete(interrupt);
+       flag_dst(&interrupt);
 }
index 489a26476ba71f05b43d51f3e732cd04a5a8874d..9cf840bc056cc429e8afc96bc0591fa0bb9f1861 100644 (file)
@@ -3,9 +3,9 @@
 
 #include <dragonstd/flag.h>
 
-extern Flag *interrupt;
-
 void interrupt_init();
 void interrupt_deinit();
 
-#endif
+extern Flag interrupt;
+
+#endif // _INTERRUPT_H_
diff --git a/src/map.c b/src/map.c
deleted file mode 100644 (file)
index 8c75756..0000000
--- a/src/map.c
+++ /dev/null
@@ -1,272 +0,0 @@
-#include <stdlib.h>
-#include <stdbool.h>
-#include <unistd.h>
-#include <math.h>
-#include <string.h>
-#include "map.h"
-#include "util.h"
-
-Map *map_create(MapCallbacks callbacks)
-{
-       Map *map = malloc(sizeof(Map));
-       pthread_rwlock_init(&map->rwlck, NULL);
-       pthread_rwlock_init(&map->cached_rwlck, NULL);
-       map->sectors = bintree_create(sizeof(v2s32), NULL);
-       map->cached = NULL;
-       map->callbacks = callbacks;
-       return map;
-}
-
-static void free_block(BintreeNode *node, void *arg)
-{
-       Map *map = arg;
-
-       if (map->callbacks.delete_block)
-               map->callbacks.delete_block(node->value);
-
-       map_free_block(node->value);
-}
-
-static void free_sector(BintreeNode *node, void *arg)
-{
-       MapSector *sector = node->value;
-
-       bintree_clear(&sector->blocks, &free_block, arg);
-       pthread_rwlock_destroy(&sector->rwlck);
-       free(sector);
-}
-
-void map_delete(Map *map)
-{
-       pthread_rwlock_destroy(&map->rwlck);
-       pthread_rwlock_destroy(&map->cached_rwlck);
-       bintree_clear(&map->sectors, &free_sector, map);
-       free(map);
-}
-
-MapSector *map_get_sector(Map *map, v2s32 pos, bool create)
-{
-       if (create)
-               pthread_rwlock_wrlock(&map->rwlck);
-       else
-               pthread_rwlock_rdlock(&map->rwlck);
-
-       BintreeNode **nodeptr = bintree_search(&map->sectors, &pos);
-
-       MapSector *sector = NULL;
-
-       if (*nodeptr) {
-               sector = (*nodeptr)->value;
-       } else if (create) {
-               sector = malloc(sizeof(MapSector));
-               pthread_rwlock_init(&sector->rwlck, NULL);
-               sector->pos = pos;
-               sector->blocks = bintree_create(sizeof(s32), NULL);
-
-               bintree_add_node(&map->sectors, nodeptr, &pos, sector);
-       }
-
-       pthread_rwlock_unlock(&map->rwlck);
-
-       return sector;
-}
-
-MapBlock *map_get_block(Map *map, v3s32 pos, bool create)
-{
-       MapBlock *cached = NULL;
-
-       pthread_rwlock_rdlock(&map->cached_rwlck);
-       cached = map->cached;
-       pthread_rwlock_unlock(&map->cached_rwlck);
-
-       if (cached && v3s32_equals(cached->pos, pos))
-               return cached;
-
-       MapSector *sector = map_get_sector(map, (v2s32) {pos.x, pos.z}, create);
-       if (! sector)
-               return NULL;
-
-       if (create)
-               pthread_rwlock_wrlock(&sector->rwlck);
-       else
-               pthread_rwlock_rdlock(&sector->rwlck);
-
-       BintreeNode **nodeptr = bintree_search(&sector->blocks, &pos.y);
-
-       MapBlock *block = NULL;
-
-       if (*nodeptr) {
-               block = (*nodeptr)->value;
-
-               pthread_mutex_lock(&block->mtx);
-               if (map->callbacks.get_block && ! map->callbacks.get_block(block, create)) {
-                       pthread_mutex_unlock(&block->mtx);
-                       block = NULL;
-               } else {
-                       pthread_mutex_unlock(&block->mtx);
-                       pthread_rwlock_wrlock(&map->cached_rwlck);
-                       map->cached = block;
-                       pthread_rwlock_unlock(&map->cached_rwlck);
-               }
-       } else if (create) {
-               bintree_add_node(&sector->blocks, nodeptr, &pos.y, block = map_allocate_block(pos));
-
-               if (map->callbacks.create_block)
-                       map->callbacks.create_block(block);
-       }
-
-       pthread_rwlock_unlock(&sector->rwlck);
-
-       return block;
-}
-
-MapBlock *map_allocate_block(v3s32 pos)
-{
-       MapBlock *block = malloc(sizeof(MapBlock));
-       block->pos = pos;
-       block->extra = NULL;
-       pthread_mutexattr_t attr;
-       pthread_mutexattr_init(&attr);
-       pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
-       pthread_mutex_init(&block->mtx, &attr);
-
-       ITERATE_MAPBLOCK block->data[x][y][z] = map_node_create(NODE_UNKNOWN, (Blob) {0, NULL});
-
-       return block;
-}
-
-void map_free_block(MapBlock *block)
-{
-       ITERATE_MAPBLOCK map_node_delete(block->data[x][y][z]);
-
-       pthread_mutex_destroy(&block->mtx);
-       free(block);
-}
-
-Blob map_serialize_block(MapBlock *block)
-{
-       bool all_air = true;
-
-       ITERATE_MAPBLOCK {
-               if (block->data[x][y][z].type != NODE_AIR) {
-                       all_air = false;
-                       break;
-               }
-       }
-
-       if (all_air)
-               return (Blob) {0, NULL};
-
-       SerializedMapBlock block_data;
-
-       ITERATE_MAPBLOCK {
-               MapNode *node = &block->data[x][y][z];
-               SerializedMapNode *node_data = &block_data.raw.nodes[x][y][z];
-
-               *node_data = (SerializedMapNode) {
-                       .type = node->type,
-                       .data = {
-                               .siz = 0,
-                               .data = NULL,
-                       },
-               };
-
-               NodeDefinition *def = &node_definitions[node->type];
-
-               if (def->serialize)
-                       def->serialize(&node_data->data, node->data);
-       }
-
-       Blob buffer = {0, NULL};
-       SerializedMapBlock_write(&buffer, &block_data);
-       SerializedMapBlock_free(&block_data);
-
-       return buffer;
-}
-
-bool map_deserialize_block(MapBlock *block, Blob buffer)
-{
-       if (buffer.siz == 0) {
-               ITERATE_MAPBLOCK
-                       block->data[x][y][z] = map_node_create(NODE_AIR, (Blob) {0, NULL});
-
-               return true;
-       }
-
-       // it's important to copy Blobs that have been malloc'd before reading from them
-       // because reading from a Blob modifies its data and size pointer,
-       // but does not free anything
-       SerializedMapBlock block_data = {0};
-       bool success = SerializedMapBlock_read(&buffer, &block_data);
-
-       if (success) ITERATE_MAPBLOCK
-               block->data[x][y][z] = map_node_create(block_data.raw.nodes[x][y][z].type, block_data.raw.nodes[x][y][z].data);
-
-       SerializedMapBlock_free(&block_data);
-       return success;
-}
-
-v3s32 map_node_to_block_pos(v3s32 pos, v3u8 *offset)
-{
-       if (offset)
-               *offset = (v3u8) {(u32) pos.x % MAPBLOCK_SIZE, (u32) pos.y % MAPBLOCK_SIZE, (u32) pos.z % MAPBLOCK_SIZE};
-       return (v3s32) {floor((double) pos.x / (double) MAPBLOCK_SIZE), floor((double) pos.y / (double) MAPBLOCK_SIZE), floor((double) pos.z / (double) MAPBLOCK_SIZE)};
-}
-
-MapNode map_get_node(Map *map, v3s32 pos)
-{
-       v3u8 offset;
-       v3s32 blockpos = map_node_to_block_pos(pos, &offset);
-       MapBlock *block = map_get_block(map, blockpos, false);
-       if (! block)
-               return map_node_create(NODE_UNLOADED, (Blob) {0, NULL});
-       return block->data[offset.x][offset.y][offset.z];
-}
-
-void map_set_node(Map *map, v3s32 pos, MapNode node, bool create, void *arg)
-{
-       v3u8 offset;
-       MapBlock *block = map_get_block(map, map_node_to_block_pos(pos, &offset), create);
-       if (block) {
-               pthread_mutex_lock(&block->mtx);
-               if (! map->callbacks.set_node || map->callbacks.set_node(block, offset, &node, arg)) {
-                       block->data[offset.x][offset.y][offset.z] = node;
-                       if (map->callbacks.after_set_node)
-                               map->callbacks.after_set_node(block, offset, arg);
-               } else {
-                       map_node_delete(node);
-               }
-               pthread_mutex_unlock(&block->mtx);
-       }
-}
-
-MapNode map_node_create(Node type, Blob buffer)
-{
-       if (type >= NODE_UNLOADED)
-               type = NODE_UNKNOWN;
-
-       NodeDefinition *def = &node_definitions[type];
-
-       MapNode node;
-       node.type = type;
-       node.data = def->data_size ? malloc(def->data_size) : NULL;
-
-       if (def->create)
-               def->create(&node);
-
-       if (def->deserialize)
-               def->deserialize(&buffer, node.data);
-
-       return node;
-}
-
-void map_node_delete(MapNode node)
-{
-       NodeDefinition *def = &node_definitions[node.type];
-
-       if (def->delete)
-               def->delete(&node);
-
-       if (node.data)
-               free(node.data);
-}
diff --git a/src/map.h b/src/map.h
deleted file mode 100644 (file)
index 9e61d0d..0000000
--- a/src/map.h
+++ /dev/null
@@ -1,74 +0,0 @@
-#ifndef _MAP_H_
-#define _MAP_H_
-
-#include <stdbool.h>
-#include <pthread.h>
-#include <dragonstd/bintree.h>
-#include <dragonstd/list.h>
-#include "types.h"
-#include "node.h"
-
-#define ITERATE_MAPBLOCK for (u8 x = 0; x < MAPBLOCK_SIZE; x++) for (u8 y = 0; y < MAPBLOCK_SIZE; y++) for (u8 z = 0; z < MAPBLOCK_SIZE; z++)
-
-typedef struct MapNode
-{
-       Node type;
-       void *data;
-} MapNode;
-
-typedef MapNode MapBlockData[MAPBLOCK_SIZE][MAPBLOCK_SIZE][MAPBLOCK_SIZE];
-
-typedef struct
-{
-       MapBlockData data;
-       v3s32 pos;
-       pthread_mutex_t mtx;
-       void *extra;
-} MapBlock;
-
-typedef struct
-{
-       pthread_rwlock_t rwlck;
-       Bintree blocks;
-       v2s32 pos;
-} MapSector;
-
-typedef struct
-{
-       void (*create_block)(MapBlock *block);
-       void (*delete_block)(MapBlock *block);
-       bool (*get_block)(MapBlock *block, bool create);
-       bool (*set_node) (MapBlock *block, v3u8 offset, MapNode *node, void *arg);
-       void (*after_set_node)(MapBlock *block, v3u8 offset, void *arg);
-} MapCallbacks;
-
-typedef struct
-{
-       pthread_rwlock_t rwlck;
-       Bintree sectors;
-       pthread_rwlock_t cached_rwlck;
-       MapBlock *cached;
-       MapCallbacks callbacks;
-} Map;
-
-Map *map_create(MapCallbacks callbacks);
-void map_delete(Map *map);
-
-MapSector *map_get_sector(Map *map, v2s32 pos, bool create);
-MapBlock *map_get_block(Map *map, v3s32 pos, bool create);
-
-MapBlock *map_allocate_block(v3s32 pos);
-void map_free_block(MapBlock *block);
-
-Blob map_serialize_block(MapBlock *block);
-bool map_deserialize_block(MapBlock *block, Blob buffer);
-
-v3s32 map_node_to_block_pos(v3s32 pos, v3u8 *offset);
-
-MapNode map_get_node(Map *map, v3s32 pos);
-void map_set_node(Map *map, v3s32 pos, MapNode node, bool create, void *arg);
-
-MapNode map_node_create(Node type, Blob buffer);
-void map_node_delete(MapNode node);
-
-#endif
index e89025ed3b23c01b203c6e4fb8bbbe266027cc3c..be1ef8f15884923e1bc1756e742a31bee876a88d 100755 (executable)
@@ -1,3 +1,3 @@
-#! /bin/sh
-LUA_PATH="../deps/dragontype/?.lua;../deps/dragontype/?/init.lua" "../deps/dragontype/typegen.lua"
+#!/bin/sh
+LUA_PATH="../deps/protogen/?.lua;../deps/protogen/?/init.lua" "../deps/protogen/protogen.lua"
 
index 5aeefea8bffa9ba20fe8d1c076d788d271c7c365..727f81cf10508fa4c2c04a38af265fe5ca313c90 100644 (file)
@@ -1,8 +1,6 @@
-#include "types.h"
-#include "map.h"
 #include "node.h"
-#include "util.h"
-#include <stdio.h>
+#include "terrain.h"
+#include "types.h"
 
 NodeDefinition node_definitions[NODE_UNLOADED] = {
        // unknown
@@ -62,56 +60,56 @@ NodeDefinition node_definitions[NODE_UNLOADED] = {
        // oak wood
        {
                .solid = true,
-               .data_size = sizeof(HSLData),
+               .data_size = sizeof(ColorData),
                .create = NULL,
                .delete = NULL,
-               .serialize = (void *) &HSLData_write,
-               .deserialize = (void *) &HSLData_read,
+               .serialize = (void *) &ColorData_write,
+               .deserialize = (void *) &ColorData_read,
        },
        // oak leaves
        {
                .solid = true,
-               .data_size = sizeof(HSLData),
+               .data_size = sizeof(ColorData),
                .create = NULL,
                .delete = NULL,
-               .serialize = (void *) &HSLData_write,
-               .deserialize = (void *) &HSLData_read,
+               .serialize = (void *) &ColorData_write,
+               .deserialize = (void *) &ColorData_read,
        },
        // pine wood
        {
                .solid = true,
-               .data_size = sizeof(HSLData),
+               .data_size = sizeof(ColorData),
                .create = NULL,
                .delete = NULL,
-               .serialize = (void *) &HSLData_write,
-               .deserialize = (void *) &HSLData_read,
+               .serialize = (void *) &ColorData_write,
+               .deserialize = (void *) &ColorData_read,
        },
        // pine leaves
        {
                .solid = true,
-               .data_size = sizeof(HSLData),
+               .data_size = sizeof(ColorData),
                .create = NULL,
                .delete = NULL,
-               .serialize = (void *) &HSLData_write,
-               .deserialize = (void *) &HSLData_read,
+               .serialize = (void *) &ColorData_write,
+               .deserialize = (void *) &ColorData_read,
        },
        // palm wood
        {
                .solid = true,
-               .data_size = sizeof(HSLData),
+               .data_size = sizeof(ColorData),
                .create = NULL,
                .delete = NULL,
-               .serialize = (void *) &HSLData_write,
-               .deserialize = (void *) &HSLData_read,
+               .serialize = (void *) &ColorData_write,
+               .deserialize = (void *) &ColorData_read,
        },
        // palm leaves
        {
                .solid = true,
-               .data_size = sizeof(HSLData),
+               .data_size = sizeof(ColorData),
                .create = NULL,
                .delete = NULL,
-               .serialize = (void *) &HSLData_write,
-               .deserialize = (void *) &HSLData_read,
+               .serialize = (void *) &ColorData_write,
+               .deserialize = (void *) &ColorData_read,
        },
        // sand
        {
index 2ed4edd6720ce4d342209a85d6f70daddeb77a9e..3e7e9307fb25202abbec7da37b444054b9e1ab1f 100644 (file)
@@ -2,12 +2,12 @@
 #define _NODE_H_
 
 #include <stdbool.h>
+#include <stddef.h>
 #include "types.h"
 
 #define NODE_DEFINITION(type) ((type) < NODE_UNLOADED ? &node_definitions[NODE_UNKNOWN] : &node_definitions[(type)]);
 
-typedef enum
-{
+typedef enum {
        NODE_UNKNOWN,       // Used for unknown nodes received from server (caused by outdated clients)
        NODE_AIR,
        NODE_GRASS,
@@ -24,17 +24,16 @@ typedef enum
        NODE_WATER,
        NODE_LAVA,
        NODE_VULCANO_STONE,
-       NODE_UNLOADED,      // Used for nodes in unloaded blocks
-} Node;
+       NODE_UNLOADED,      // Used for nodes in unloaded chunks
+} NodeType;
 
-struct MapNode;
+struct TerrainNode;
 
-typedef struct
-{
+typedef struct {
        bool solid;
        size_t data_size;
-       void (*create)(struct MapNode *node);
-       void (*delete)(struct MapNode *node);
+       void (*create)(struct TerrainNode *node);
+       void (*delete)(struct TerrainNode *node);
        void (*serialize)(Blob *buffer, void *data);
        void (*deserialize)(Blob *buffer, void *data);
 } NodeDefinition;
index 0213c68b00f182ee0bbb9eb3e9890f30c0f6cea2..15a63c995f85baf05f647ffc60303b2c458c728d 100644 (file)
@@ -4,8 +4,9 @@
 #include <perlin/perlin.h>
 #include "types.h"
 
-typedef enum
-{
+#define U32(x) (((u32) 1 << 31) + (x))
+
+typedef enum {
        SO_NONE,
        SO_HEIGHT,
        SO_MOUNTAIN,
@@ -35,4 +36,4 @@ typedef enum
 
 extern s32 seed;
 
-#endif
+#endif // _PERLIN_H_
diff --git a/src/physics.c b/src/physics.c
new file mode 100644 (file)
index 0000000..8f117b4
--- /dev/null
@@ -0,0 +1,125 @@
+#include <math.h>
+#include "physics.h"
+
+static aabb3f64 move_box(aabb3f32 box, v3f64 pos)
+{
+       return (aabb3f64) {
+               {pos.x + box.min.x, pos.y + box.min.y, pos.z + box.min.z},
+               {pos.x + box.max.x, pos.y + box.max.y, pos.z + box.max.z},
+       };
+}
+
+static aabb3s32 round_box(aabb3f64 box)
+{
+       return (aabb3s32) {
+               {floor(box.min.x + 0.5), floor(box.min.y + 0.5), floor(box.min.z + 0.5)},
+               { ceil(box.max.x - 0.5),  ceil(box.max.y - 0.5),  ceil(box.max.z - 0.5)},
+       };
+}
+
+static bool is_solid(Terrain *terrain, s32 x, s32 y, s32 z)
+{
+       NodeType node = terrain_get_node(terrain, (v3s32) {x, y, z}).type;
+       return node == NODE_UNLOADED || node_definitions[node].solid;
+}
+
+bool physics_ground(Terrain *terrain, bool collide, aabb3f32 box, v3f64 *pos, v3f64 *vel)
+{
+       if (!collide)
+               return false;
+
+       if (vel->y != 0.0)
+               return false;
+
+       aabb3f64 mbox = move_box(box, *pos);
+       mbox.min.y -= 0.5;
+
+       aabb3s32 rbox = round_box(mbox);
+
+       if (mbox.min.y - (f64) rbox.min.y > 0.01)
+               return false;
+
+       for (s32 x = rbox.min.x; x <= rbox.max.x; x++)
+               for (s32 z = rbox.min.z; z <= rbox.max.z; z++)
+                       if (is_solid(terrain, x, rbox.min.y, z))
+                               return true;
+
+       return false;
+}
+
+bool physics_step(Terrain *terrain, bool collide, aabb3f32 box, v3f64 *pos, v3f64 *vel, v3f64 *acc, f64 t)
+{
+       v3f64 old_pos = *pos;
+
+       f64 *x = &pos->x;
+       f64 *v = &vel->x;
+       f64 *a = &acc->x;
+
+       f32 *min = &box.min.x;
+       f32 *max = &box.max.x;
+
+       static u8 idx[3][3] = {
+               {0, 1, 2},
+               {1, 0, 2},
+               {2, 0, 1},
+       };
+
+       for (u8 i = 0; i < 3; i++) {
+               f64 v_old = v[i];
+               v[i] += a[i] * t;
+               f64 v_cur = (v[i] + v_old) / 2.0;
+               if (v_cur == 0.0)
+                       continue;
+
+               f64 x_old = x[i];
+               x[i] += v_cur * t;
+               if (!collide)
+                       continue;
+
+               aabb3s32 box_rnd = round_box(move_box(box, *pos));
+
+               s32 dir;
+               f32 off;
+               s32 *min_rnd = &box_rnd.min.x;
+               s32 *max_rnd = &box_rnd.max.x;
+
+               if (v[i] > 0.0) {
+                       dir = +1;
+                       off = max[i];
+
+                       min_rnd[i] = ceil(x_old + off + 0.5);
+                       max_rnd[i] = floor(x[i] + off + 0.5);
+               } else {
+                       dir = -1;
+                       off = min[i];
+
+                       min_rnd[i] = floor(x_old + off - 0.5);
+                       max_rnd[i] =   ceil(x[i] + off - 0.5);
+               }
+
+               max_rnd[i] += dir;
+
+               u8 i_a = idx[i][0]; // = i
+               u8 i_b = idx[i][1];
+               u8 i_c = idx[i][2];
+
+               for (s32 a = min_rnd[i_a]; a != max_rnd[i_a]; a += dir)
+               for (s32 b = min_rnd[i_b]; b <= max_rnd[i_b]; b++)
+               for (s32 c = min_rnd[i_c]; c <= max_rnd[i_c]; c++)      {
+                       s32 p[3];
+                       p[i_a] = a;
+                       p[i_b] = b;
+                       p[i_c] = c;
+
+                       if (is_solid(terrain, p[0], p[1], p[2])) {
+                               x[i] = (f64) a - off - 0.5 * (f64) dir;
+                               v[i] = 0.0;
+                               goto done;
+                       }
+               }
+
+               done: continue;
+       }
+
+       return !v3f64_equals(*pos, old_pos);
+}
diff --git a/src/physics.h b/src/physics.h
new file mode 100644 (file)
index 0000000..083cc09
--- /dev/null
@@ -0,0 +1,11 @@
+#ifndef _PHYSICS_H_
+#define _PHYSICS_H_
+
+#include <stdbool.h>
+#include "terrain.h"
+#include "types.h"
+
+bool physics_ground(Terrain *terrain, bool collide, aabb3f32 box, v3f64 *pos, v3f64 *vel);
+bool physics_step  (Terrain *terrain, bool collide, aabb3f32 box, v3f64 *pos, v3f64 *vel, v3f64 *acc, f64 t);
+
+#endif // _PHYSICS_H_
index 44a17c39c761a0f399f9f875df0960be9dd40e79..22f88a66e58bf31449f2b60ae471601651e628ed 100644 (file)
@@ -1,14 +1,14 @@
 #include <math.h>
 #include "server/biomes.h"
-#include "server/mapgen.h"
-#include "server/server_map.h"
-#include "util.h"
+#include "server/server_terrain.h"
+#include "server/terrain_gen.h"
 
 Biome get_biome(v2s32 pos, f64 *factor)
 {
-       for (Biome i = 0; i < BIOME_COUNT; i++) {
+       for (Biome i = 0; i < COUNT_BIOME; i++) {
                BiomeDef *def = &biomes[i];
-               f64 f = def->probability == 1.0 ? 1.0 : (smooth2d(U32(pos.x) / def->threshold, U32(pos.y) / def->threshold, 0, seed + def->offset) * 0.5 - 0.5 + def->probability) / def->probability;
+               f64 f = def->probability == 1.0 ? 1.0
+                       : (smooth2d(U32(pos.x) / def->threshold, U32(pos.y) / def->threshold, 0, seed + def->offset) * 0.5 - 0.5 + def->probability) / def->probability;
 
                if (f > 0.0) {
                        if (factor)
@@ -17,74 +17,71 @@ Biome get_biome(v2s32 pos, f64 *factor)
                }
        }
 
-       return BIOME_COUNT;
+       return COUNT_BIOME;
 }
 
 // mountain biome
 
-static s32 height_mountain(v2s32 pos, f64 factor, f32 height, unused void *row_data, unused void *block_data)
+static s32 height_mountain(BiomeArgsHeight *args)
 {
-       return pow((height + 96) * pow(((smooth2d(U32(pos.x) / 48.0, U32(pos.y) / 48.0, 0, seed + SO_MOUNTAIN_HEIGHT) + 1.0) * 256.0 + 128.0), factor), 1.0 / (factor + 1.0)) - 96;
+       return pow((args->height + 96) * pow(((smooth2d(U32(args->pos.x) / 48.0, U32(args->pos.y) / 48.0, 0, seed + SO_MOUNTAIN_HEIGHT) + 1.0) * 256.0 + 128.0), args->factor), 1.0 / (args->factor + 1.0)) - 96;
 }
 
-static Node generate_mountain(unused v3s32 pos, s32 diff, unused f64 humidity, unused f64 temperature, unused f64 factor, unused MapBlock *block, unused List *changed_blocks, unused void *row_data, unused void *block_data)
+static NodeType generate_mountain(BiomeArgsGenerate *args)
 {
-       return diff <= 0 ? NODE_STONE : NODE_AIR;
+       return args->diff <= 0 ? NODE_STONE : NODE_AIR;
 }
 
 // ocean biome
 
-typedef enum
-{
-       OL_BEACH_EDGE,
-       OL_BEACH,
-       OL_OCEAN,
-       OL_DEEP_OCEAN,
-       OL_COUNT
+typedef enum {
+       OCEAN_EDGE,
+       OCEAN_BEACH,
+       OCEAN_MAIN,
+       OCEAN_DEEP,
+       COUNT_OCEAN
 } OceanLevel;
 
-static f64 ocean_level_start[OL_COUNT] = {
+static f64 ocean_level_start[COUNT_OCEAN] = {
        0.0,
        0.1,
        0.2,
        0.5,
 };
 
-typedef struct
-{
+typedef struct {
        bool has_vulcano;
        v2s32 vulcano_pos;
-} OceanBlockData;
+} OceanChunkData;
 
-typedef struct
-{
+typedef struct {
        bool vulcano;
        bool vulcano_crater;
        s32 vulcano_height;
        s32 vulcano_crater_top;
-       Node vulcano_stone;
+       NodeType vulcano_stone;
 } OceanRowData;
 
 static const f64 vulcano_radius = 256.0;
-static const f64 vulcano_block_offset = vulcano_radius * 2.0 / MAPBLOCK_SIZE;
+static const f64 vulcano_chunk_offset = vulcano_radius * 2.0 / CHUNK_SIZE;
 
 static OceanLevel get_ocean_level(f64 factor)
 {
-       if (factor >= ocean_level_start[OL_DEEP_OCEAN])
-               return OL_DEEP_OCEAN;
-       else if (factor >= ocean_level_start[OL_OCEAN])
-               return OL_OCEAN;
-       else if (factor >= ocean_level_start[OL_BEACH])
-               return OL_BEACH;
-
-       return OL_BEACH_EDGE;
+       if (factor >= ocean_level_start[OCEAN_DEEP])
+               return OCEAN_DEEP;
+       else if (factor >= ocean_level_start[OCEAN_MAIN])
+               return OCEAN_MAIN;
+       else if (factor >= ocean_level_start[OCEAN_BEACH])
+               return OCEAN_BEACH;
+
+       return OCEAN_EDGE;
 }
 
 static f64 get_ocean_level_factor(f64 factor, OceanLevel level)
 {
        f64 start, end;
        start = ocean_level_start[level];
-       end = ++level == OL_COUNT ? 1.0 : ocean_level_start[level];
+       end = ++level == COUNT_OCEAN ? 1.0 : ocean_level_start[level];
 
        return (factor - start) / (end - start);
 }
@@ -92,13 +89,16 @@ static f64 get_ocean_level_factor(f64 factor, OceanLevel level)
 static bool is_vulcano(v2s32 pos)
 {
        f64 factor;
-       return noise2d(pos.x, pos.y, 0, seed + SO_VULCANO) > 0.0 && get_biome((v2s32) {pos.x * MAPBLOCK_SIZE, pos.y * MAPBLOCK_SIZE}, &factor) == BIOME_OCEAN && get_ocean_level(factor) == OL_DEEP_OCEAN;
+
+       return noise2d(pos.x, pos.y, 0, seed + SO_VULCANO) > 0.0
+               && get_biome((v2s32) {pos.x * CHUNK_SIZE, pos.y * CHUNK_SIZE}, &factor) == BIOME_OCEAN
+               && get_ocean_level(factor) == OCEAN_DEEP;
 }
 
 static bool find_near_vulcano(v2s32 pos, v2s32 *result)
 {
-       f64 x = pos.x / vulcano_block_offset;
-       f64 z = pos.y / vulcano_block_offset;
+       f64 x = pos.x / vulcano_chunk_offset;
+       f64 z = pos.y / vulcano_chunk_offset;
 
        s32 lx, lz;
        lx = floor(x);
@@ -130,17 +130,17 @@ static f64 distance(v2s32 a, v2s32 b)
 static s32 calculate_ocean_floor(f64 factor, s32 height)
 {
        switch (get_ocean_level(factor)) {
-               case OL_BEACH_EDGE:
-                       return f64_mix(height + 1, 0, pow(get_ocean_level_factor(factor, OL_BEACH_EDGE), 0.8));
+               case OCEAN_EDGE:
+                       return f64_mix(height + 1, 0, pow(get_ocean_level_factor(factor, OCEAN_EDGE), 0.8));
 
-               case OL_BEACH:
+               case OCEAN_BEACH:
                        return 0;
 
-               case OL_OCEAN:
-                       return f64_mix(0, -10, pow(get_ocean_level_factor(factor, OL_OCEAN), 0.5));
+               case OCEAN_MAIN:
+                       return f64_mix(0, -10, pow(get_ocean_level_factor(factor, OCEAN_MAIN), 0.5));
 
-               case OL_DEEP_OCEAN:
-                       return f64_mix(-10, -50, pow(get_ocean_level_factor(factor, OL_DEEP_OCEAN), 0.5));
+               case OCEAN_DEEP:
+                       return f64_mix(-10, -50, pow(get_ocean_level_factor(factor, OCEAN_DEEP), 0.5));
 
                default:
                        break;
@@ -149,61 +149,66 @@ static s32 calculate_ocean_floor(f64 factor, s32 height)
        return height;
 }
 
-static void preprocess_block_ocean(MapBlock *block, unused List *changed_blocks, void *block_data)
+static void chunk_ocean(BiomeArgsChunk *args)
 {
-       OceanBlockData *data = block_data;
-
+       OceanChunkData *chunk_data = args->chunk_data;
        v2s32 vulcano_pos;
-       if ((data->has_vulcano = find_near_vulcano((v2s32) {block->pos.x, block->pos.z}, &vulcano_pos)))
-               data->vulcano_pos = (v2s32) {vulcano_pos.x * MAPBLOCK_SIZE, vulcano_pos.y * MAPBLOCK_SIZE};
+
+       if ((chunk_data->has_vulcano = find_near_vulcano((v2s32) {args->chunk->pos.x, args->chunk->pos.z}, &vulcano_pos)))
+               chunk_data->vulcano_pos = (v2s32) {vulcano_pos.x * CHUNK_SIZE, vulcano_pos.y * CHUNK_SIZE};
 }
 
-static void preprocess_row_ocean(v2s32 pos, unused f64 factor, void *row_data, void *block_data)
+static void row_ocean(BiomeArgsRow *args)
 {
-       OceanRowData *rdata = row_data;
-       OceanBlockData *bdata = block_data;
-       rdata->vulcano = false;
+       OceanChunkData *chunk_data = args->chunk_data;
+       OceanRowData *row_data = args->row_data;
 
-       if (bdata->has_vulcano) {
-               f64 dist = distance(pos, bdata->vulcano_pos);
+       row_data->vulcano = false;
+
+       if (chunk_data->has_vulcano) {
+               f64 dist = distance(args->pos, chunk_data->vulcano_pos);
 
                if (dist < vulcano_radius) {
                        f64 crater_factor = pow(asin(1.0 - dist / vulcano_radius), 2.0);
-                       f64 vulcano_height = (pnoise2d(U32(pos.x) / 100.0, U32(pos.y) / 100.0, 0.2, 2, seed + SO_VULCANO_HEIGHT) * 0.5 + 0.5) * 128.0 * crater_factor + 1.0 - 30.0;
+                       f64 vulcano_height = (pnoise2d(U32(args->pos.x) / 100.0, U32(args->pos.y) / 100.0, 0.2, 2, seed + SO_VULCANO_HEIGHT) * 0.5 + 0.5) * 128.0 * crater_factor + 1.0 - 30.0;
                        bool is_crater = vulcano_height > 0;
 
-                       if (! is_crater)
+                       if (!is_crater)
                                vulcano_height = f64_min(vulcano_height + 5.0, 0.0);
 
                        if (vulcano_height < 0)
                                vulcano_height *= 2.0;
 
-                       rdata->vulcano = true;
-                       rdata->vulcano_crater = is_crater;
-                       rdata->vulcano_height = floor(vulcano_height + 0.5);
-                       rdata->vulcano_crater_top = 50 + floor((pnoise2d(U32(pos.x) / 3.0, U32(pos.y) / 3.0, 0.0, 1, seed + SO_VULCANO_CRATER_TOP) * 0.5 + 0.5) * 3.0 + 0.5);
-                       rdata->vulcano_stone = is_crater ? ((pnoise2d(U32(pos.x) / 16.0, U32(pos.y) / 16.0, 0.85, 3, seed + SO_VULCANO_STONE) * 0.5 + 0.5) * crater_factor > 0.4 ? NODE_VULCANO_STONE : NODE_STONE) : NODE_SAND;
+                       row_data->vulcano = true;
+                       row_data->vulcano_crater = is_crater;
+                       row_data->vulcano_height = floor(vulcano_height + 0.5);
+                       row_data->vulcano_crater_top = 50 + floor((pnoise2d(U32(args->pos.x) / 3.0, U32(args->pos.y) / 3.0, 0.0, 1, seed + SO_VULCANO_CRATER_TOP) * 0.5 + 0.5) * 3.0 + 0.5);
+                       row_data->vulcano_stone = is_crater
+                               ? ((pnoise2d(U32(args->pos.x) / 16.0, U32(args->pos.y) / 16.0, 0.85, 3, seed + SO_VULCANO_STONE) * 0.5 + 0.5) * crater_factor > 0.4
+                                       ? NODE_VULCANO_STONE
+                                       : NODE_STONE)
+                               : NODE_SAND;
                }
        }
 }
 
-static s32 height_ocean(unused v2s32 pos, f64 factor, f32 height, void *row_data, unused void *block_data)
+static s32 height_ocean(BiomeArgsHeight *args)
 {
-       OceanRowData *rdata = row_data;
-       s32 ocean_floor = calculate_ocean_floor(factor, height);
+       OceanRowData *row_data = args->row_data;
 
-       return rdata->vulcano ? f64_max(ocean_floor, rdata->vulcano_height) : ocean_floor;
+       s32 ocean_floor = calculate_ocean_floor(args->factor, args->height);
+       return row_data->vulcano ? f64_max(ocean_floor, row_data->vulcano_height) : ocean_floor;
 }
 
-Node ocean_get_node_at(v3s32 pos, s32 diff, void *row_data)
+NodeType ocean_get_node_at(v3s32 pos, s32 diff, void *_row_data)
 {
-       OceanRowData *rdata = row_data;
+       OceanRowData *row_data = _row_data;
 
-       if (rdata->vulcano && rdata->vulcano_crater) {
+       if (row_data->vulcano && row_data->vulcano_crater) {
                if (diff <= -5)
                        return pos.y <= 45 ? NODE_LAVA : NODE_AIR;
                else if (diff <= 0)
-                       return pos.y <= rdata->vulcano_crater_top ? rdata->vulcano_stone : NODE_AIR;
+                       return pos.y <= row_data->vulcano_crater_top ? row_data->vulcano_stone : NODE_AIR;
                else
                        return NODE_AIR;
        } else {
@@ -218,9 +223,9 @@ Node ocean_get_node_at(v3s32 pos, s32 diff, void *row_data)
        return NODE_AIR;
 }
 
-static Node generate_ocean(v3s32 pos, s32 diff, unused f64 humidity, unused f64 temperature, unused f64 factor, unused MapBlock *block, unused List *changed_blocks, void *row_data, unused void *block_data)
+static NodeType generate_ocean(BiomeArgsGenerate *args)
 {
-       return ocean_get_node_at(pos, diff, row_data);
+       return ocean_get_node_at(args->pos, args->diff, args->row_data);
 }
 
 // hills biome
@@ -235,27 +240,27 @@ static bool boulder_touching_ground(v3s32 pos, s32 diff)
        return true;
 }
 
-static s32 height_hills(unused v2s32 pos, unused f64 factor, f32 height, unused void *row_data, unused void *block_data)
+static s32 height_hills(BiomeArgsHeight *args)
 {
-       return height;
+       return args->height;
 }
 
-static Node generate_hills(v3s32 pos, s32 diff, unused f64 humidity, unused f64 temperature, unused f64 factor, unused MapBlock *block, unused List *changed_blocks, unused void *row_data, unused void *block_data)
+static NodeType generate_hills(BiomeArgsGenerate *args)
 {
-       if (boulder_touching_ground(pos, diff))
+       if (boulder_touching_ground(args->pos, args->diff))
                return NODE_STONE;
 
-       if (diff <= -5)
+       if (args->diff <= -5)
                return NODE_STONE;
-       else if (diff <= -1)
+       else if (args->diff <= -1)
                return NODE_DIRT;
-       else if (diff <= 0)
+       else if (args->diff <= 0)
                return NODE_GRASS;
 
        return NODE_AIR;
 }
 
-BiomeDef biomes[BIOME_COUNT] = {
+BiomeDef biomes[COUNT_BIOME] = {
        {
                .probability = 0.2,
                .offset = SO_MOUNTAIN,
@@ -263,10 +268,10 @@ BiomeDef biomes[BIOME_COUNT] = {
                .snow = true,
                .height = &height_mountain,
                .generate = &generate_mountain,
-               .block_data_size = 0,
-               .preprocess_block = NULL,
+               .chunk_data_size = 0,
+               .chunk = NULL,
                .row_data_size = 0,
-               .preprocess_row = NULL,
+               .row = NULL,
        },
        {
                .probability = 0.2,
@@ -275,10 +280,10 @@ BiomeDef biomes[BIOME_COUNT] = {
                .snow = false,
                .height = &height_ocean,
                .generate = &generate_ocean,
-               .block_data_size = sizeof(OceanBlockData),
-               .preprocess_block = &preprocess_block_ocean,
+               .chunk_data_size = sizeof(OceanChunkData),
+               .chunk = &chunk_ocean,
                .row_data_size = sizeof(OceanRowData),
-               .preprocess_row = &preprocess_row_ocean,
+               .row = &row_ocean,
        },
        {
                .probability = 1.0,
@@ -287,9 +292,9 @@ BiomeDef biomes[BIOME_COUNT] = {
                .snow = true,
                .height = &height_hills,
                .generate = &generate_hills,
-               .block_data_size = 0,
-               .preprocess_block = NULL,
+               .chunk_data_size = 0,
+               .chunk = NULL,
                .row_data_size = 0,
-               .preprocess_row = NULL,
+               .row = NULL,
        },
 };
index fb440491f19f186af44e1e56d8ef15aeabfdcc07..3994100713b16a9eb5506c20b45a949b90b4c628 100644 (file)
@@ -1,35 +1,66 @@
 #ifndef _BIOMES_H_
 #define _BIOMES_H_
 
-#include "map.h"
 #include "perlin.h"
+#include "terrain.h"
 #include "types.h"
 
-typedef enum
-{
+typedef enum {
        BIOME_MOUNTAIN,
        BIOME_OCEAN,
        BIOME_HILLS,
-       BIOME_COUNT,
+       COUNT_BIOME,
 } Biome;
 
-typedef struct
-{
+typedef struct {
+       TerrainChunk *chunk;
+       List *changed_chunks;
+       void *chunk_data;
+} BiomeArgsChunk;
+
+typedef struct {
+       v2s32 pos;
+       f64 factor;
+       void *row_data;
+       void *chunk_data;
+} BiomeArgsRow;
+
+typedef struct {
+       v2s32 pos;
+       f64 factor;
+       f32 height;
+       void *row_data;
+       void *chunk_data;
+} BiomeArgsHeight;
+
+typedef struct {
+       v3s32 pos;
+       s32 diff;
+       f64 humidity;
+       f64 temperature;
+       f64 factor;
+       TerrainChunk *chunk;
+       List *changed_chunks;
+       void *row_data;
+       void *chunk_data;
+} BiomeArgsGenerate;
+
+typedef struct {
        f64 probability;
        SeedOffset offset;
        f64 threshold;
        bool snow;
-       s32 (*height)(v2s32 pos, f64 factor, f32 height, void *row_data, void *block_data);
-       Node (*generate)(v3s32 pos, s32 diff, f64 humidity, f64 temperature, f64 factor, MapBlock *block, List *changed_blocks, void *row_data, void *block_data);
-       size_t block_data_size;
-       void (*preprocess_block)(MapBlock *block, List *changed_blocks, void *block_data);
+       s32 (*height)(BiomeArgsHeight *args);
+       NodeType (*generate)(BiomeArgsGenerate *args);
+       size_t chunk_data_size;
+       void (*chunk)(BiomeArgsChunk *args);
        size_t row_data_size;
-       void (*preprocess_row)(v2s32 pos, f64 factor, void *row_data, void *block_data);
+       void (*row)(BiomeArgsRow *args);
 } BiomeDef;
 
 extern BiomeDef biomes[];
 
 Biome get_biome(v2s32 pos, f64 *factor);
-Node ocean_get_node_at(v3s32 pos, s32 diff, void *row_data);
+NodeType ocean_get_node_at(v3s32 pos, s32 diff, void *_row_data);
 
-#endif
+#endif // _BIOMES_H_
index 3d9eb92ba689000c7a2c30bbe1a4802dd3d0293d..3d06edb9ab38ffdf714739dffe22b81f177319f9 100644 (file)
@@ -1,16 +1,15 @@
-#include <stdio.h>
 #include <endian.h/endian.h>
+#include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sqlite3.h>
 #include <time.h>
 #include "day.h"
 #include "server/database.h"
-#include "server/server_map.h"
+#include "server/server_terrain.h"
 #include "perlin.h"
-#include "util.h"
 
-static sqlite3 *map_database;
+static sqlite3 *terrain_database;
 static sqlite3 *meta_database;
 static sqlite3 *players_database;
 
@@ -23,24 +22,24 @@ static inline sqlite3_stmt *prepare_statement(sqlite3 *database, const char *sql
        return sqlite3_prepare_v2(database, sql, -1, &stmt, NULL) == SQLITE_OK ? stmt : NULL;
 }
 
-// print SQLite3 error message for failed block SQL statement
-static inline void print_block_error(MapBlock *block, const char *action)
+// print SQLite3 error message for failed chunk SQL statement
+static inline void print_chunk_error(TerrainChunk *chunk, const char *action)
 {
-       fprintf(stderr, "Database error with %s block at (%d, %d, %d): %s\n", action, block->pos.x, block->pos.y, block->pos.z, sqlite3_errmsg(map_database));
+       fprintf(stderr, "[warning] failed %s chunk at (%d, %d, %d): %s\n", action, chunk->pos.x, chunk->pos.y, chunk->pos.z, sqlite3_errmsg(terrain_database));
 }
 
-// prepare a SQLite3 block statement and bind the position
-static sqlite3_stmt *prepare_block_statement(MapBlock *block, const char *action, const char *sql)
+// prepare a SQLite3 chunk statement and bind the position
+static sqlite3_stmt *prepare_chunk_statement(TerrainChunk *chunk, const char *action, const char *sql)
 {
-       sqlite3_stmt *stmt;
+       sqlite3_stmt *stmt = prepare_statement(terrain_database, sql);
 
-       if (! (stmt = prepare_statement(map_database, sql))) {
-               print_block_error(block, action);
+       if (!stmt) {
+               print_chunk_error(chunk, action);
                return NULL;
        }
 
        Blob buffer = {0, NULL};
-       v3s32_write(&buffer, &block->pos);
+       v3s32_write(&buffer, &chunk->pos);
 
        sqlite3_bind_blob(stmt, 1, buffer.data, buffer.siz, &free);
 
@@ -56,6 +55,15 @@ static inline void bind_v3f64(sqlite3_stmt *stmt, int idx, v3f64 pos)
        sqlite3_bind_blob(stmt, idx, buffer.data, buffer.siz, &free);
 }
 
+// bind v3f32 to sqlite3 statement
+static inline void bind_v3f32(sqlite3_stmt *stmt, int idx, v3f32 pos)
+{
+       Blob buffer = {0, NULL};
+       v3f32_write(&buffer, &pos);
+
+       sqlite3_bind_blob(stmt, idx, buffer.data, buffer.siz, &free);
+}
+
 // public functions
 
 // open and initialize SQLite3 databases
@@ -66,20 +74,20 @@ bool database_init()
                const char *path;
                const char *init;
        } databases[3] = {
-               {&map_database,     "map.sqlite",     "CREATE TABLE IF NOT EXISTS map     (pos  BLOB PRIMARY KEY, generated INTEGER, data BLOB, mgsb BLOB);"},
+               {&terrain_database, "terrain.sqlite", "CREATE TABLE IF NOT EXISTS terrain (pos  BLOB PRIMARY KEY, generated INTEGER, data BLOB, tgsb BLOB);"},
                {&meta_database,    "meta.sqlite",    "CREATE TABLE IF NOT EXISTS meta    (key  TEXT PRIMARY KEY, value INTEGER                          );"},
-               {&players_database, "players.sqlite", "CREATE TABLE IF NOT EXISTS players (name TEXT PRIMARY KEY, pos BLOB                               );"},
+               {&players_database, "players.sqlite", "CREATE TABLE IF NOT EXISTS players (name TEXT PRIMARY KEY, pos BLOB, rot BLOB                     );"},
        };
 
        for (int i = 0; i < 3; i++) {
                if (sqlite3_open_v2(databases[i].path, databases[i].handle, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX, NULL) != SQLITE_OK) {
-                       fprintf(stderr, "Failed to open %s: %s\n", databases[i].path, sqlite3_errmsg(*databases[i].handle));
+                       fprintf(stderr, "[error] failed to open %s: %s\n", databases[i].path, sqlite3_errmsg(*databases[i].handle));
                        return false;
                }
 
                char *err;
                if (sqlite3_exec(*databases[i].handle, databases[i].init, NULL, NULL, &err) != SQLITE_OK) {
-                       fprintf(stderr, "Failed to initialize %s: %s\n", databases[i].path, err);
+                       fprintf(stderr, "[error] failed initializing %s: %s\n", databases[i].path, err);
                        sqlite3_free(err);
                        return false;
                }
@@ -105,68 +113,68 @@ bool database_init()
        return true;
 }
 
-// close database
+// close databases
 void database_deinit()
 {
        database_save_meta("time_of_day", (s64) get_time_of_day());
 
-       sqlite3_close(map_database);
+       sqlite3_close(terrain_database);
        sqlite3_close(meta_database);
        sqlite3_close(players_database);
 }
 
-// load a block from map database (initializes state, mgs buffer and data), returns false on failure
-bool database_load_block(MapBlock *block)
+// load a chunk from terrain database (initializes state, tgs buffer and data), returns false on failure
+bool database_load_chunk(TerrainChunk *chunk)
 {
-       sqlite3_stmt *stmt;
+       sqlite3_stmt *stmt = prepare_chunk_statement(chunk, "loading", "SELECT generated, data, tgsb FROM terrain WHERE pos=?");
 
-       if (! (stmt = prepare_block_statement(block, "loading", "SELECT generated, data, mgsb FROM map WHERE pos=?")))
+       if (!stmt)
                return false;
 
        int rc = sqlite3_step(stmt);
        bool found = rc == SQLITE_ROW;
 
        if (found) {
-               MapBlockExtraData *extra = block->extra;
+               TerrainChunkMeta *meta = chunk->extra;
 
-               extra->state = sqlite3_column_int(stmt, 0) ? MBS_READY : MBS_CREATED;
-               Blob_read(             &(Blob) {sqlite3_column_bytes(stmt, 1), (void *) sqlite3_column_blob(stmt, 1)}, &extra->data);
-               MapgenStageBuffer_read(&(Blob) {sqlite3_column_bytes(stmt, 2), (void *) sqlite3_column_blob(stmt, 2)}, &extra->mgsb);
+               meta->state = sqlite3_column_int(stmt, 0) ? CHUNK_READY : CHUNK_CREATED;
+               Blob_read(                 &(Blob) {sqlite3_column_bytes(stmt, 1), (void *) sqlite3_column_blob(stmt, 1)}, &meta->data);
+               TerrainGenStageBuffer_read(&(Blob) {sqlite3_column_bytes(stmt, 2), (void *) sqlite3_column_blob(stmt, 2)}, &meta->tgsb);
 
-               if (! map_deserialize_block(block, extra->data)) {
-                       fprintf(stderr, "Failed to load block at (%d, %d, %d)\n", block->pos.x, block->pos.y, block->pos.z);
+               if (!terrain_deserialize_chunk(chunk, meta->data)) {
+                       fprintf(stderr, "[error] failed deserializing chunk at (%d, %d, %d)\n", chunk->pos.x, chunk->pos.y, chunk->pos.z);
                        exit(EXIT_FAILURE);
                }
        } else if (rc != SQLITE_DONE) {
-               print_block_error(block, "loading");
+               print_chunk_error(chunk, "loading");
        }
 
        sqlite3_finalize(stmt);
        return found;
 }
 
-// save a block to database
-void database_save_block(MapBlock *block)
+// save a chunk to terrain database
+void database_save_chunk(TerrainChunk *chunk)
 {
-       sqlite3_stmt *stmt;
+       sqlite3_stmt *stmt = prepare_chunk_statement(chunk, "saving", "REPLACE INTO terrain (pos, generated, data, tgsb) VALUES(?1, ?2, ?3, ?4)");
 
-       if (! (stmt = prepare_block_statement(block, "saving", "REPLACE INTO map (pos, generated, data, mgsb) VALUES(?1, ?2, ?3, ?4)")))
+       if (!stmt)
                return;
 
-       MapBlockExtraData *extra = block->extra;
+       TerrainChunkMeta *meta = chunk->extra;
 
        Blob data = {0, NULL};
-       Blob_write(&data, &extra->data);
+       Blob_write(&data, &meta->data);
 
-       Blob mgsb = {0, NULL};
-       MapgenStageBuffer_write(&mgsb, &extra->mgsb);
+       Blob tgsb = {0, NULL};
+       TerrainGenStageBuffer_write(&tgsb, &meta->tgsb);
 
-       sqlite3_bind_int(stmt, 2, extra->state > MBS_CREATED);
+       sqlite3_bind_int(stmt, 2, meta->state > CHUNK_CREATED);
        sqlite3_bind_blob(stmt, 3, data.data, data.siz, &free);
-       sqlite3_bind_blob(stmt, 4, mgsb.data, mgsb.siz, &free);
+       sqlite3_bind_blob(stmt, 4, tgsb.data, tgsb.siz, &free);
 
        if (sqlite3_step(stmt) != SQLITE_DONE)
-               print_block_error(block, "saving");
+               print_chunk_error(chunk, "saving");
 
        sqlite3_finalize(stmt);
 }
@@ -174,10 +182,10 @@ void database_save_block(MapBlock *block)
 // load a meta entry
 bool database_load_meta(const char *key, s64 *value_ptr)
 {
-       sqlite3_stmt *stmt;
+       sqlite3_stmt *stmt = prepare_statement(meta_database, "SELECT value FROM meta WHERE key=?");
 
-       if (! (stmt = prepare_statement(meta_database, "SELECT value FROM meta WHERE key=?"))) {
-               fprintf(stderr, "Database error with loading meta %s: %s\n", key, sqlite3_errmsg(meta_database));
+       if (!stmt) {
+               fprintf(stderr, "[warning] failed loading meta %s: %s\n", key, sqlite3_errmsg(meta_database));
                return false;
        }
 
@@ -189,7 +197,7 @@ bool database_load_meta(const char *key, s64 *value_ptr)
        if (found)
                *value_ptr = sqlite3_column_int64(stmt, 0);
        else if (rc != SQLITE_DONE)
-               fprintf(stderr, "Database error with loading meta %s: %s\n", key, sqlite3_errmsg(meta_database));
+               fprintf(stderr, "[warning] failed loading meta %s: %s\n", key, sqlite3_errmsg(meta_database));
 
        sqlite3_finalize(stmt);
        return found;
@@ -198,10 +206,10 @@ bool database_load_meta(const char *key, s64 *value_ptr)
 // save / update a meta entry
 void database_save_meta(const char *key, s64 value)
 {
-       sqlite3_stmt *stmt;
+       sqlite3_stmt *stmt = prepare_statement(meta_database, "REPLACE INTO meta (key, value) VALUES(?1, ?2)");
 
-       if (! (stmt = prepare_statement(meta_database, "REPLACE INTO meta (key, value) VALUES(?1, ?2)"))) {
-               fprintf(stderr, "Database error with saving meta %s: %s\n", key, sqlite3_errmsg(meta_database));
+       if (!stmt) {
+               fprintf(stderr, "[warning] failed saving meta %s: %s\n", key, sqlite3_errmsg(meta_database));
                return;
        }
 
@@ -209,18 +217,18 @@ void database_save_meta(const char *key, s64 value)
        sqlite3_bind_int64(stmt, 2, value);
 
        if (sqlite3_step(stmt) != SQLITE_DONE)
-               fprintf(stderr, "Database error with saving meta %s: %s\n", key, sqlite3_errmsg(meta_database));
+               fprintf(stderr, "[warning] failed saving meta %s: %s\n", key, sqlite3_errmsg(meta_database));
 
        sqlite3_finalize(stmt);
 }
 
 // load player data from database
-bool database_load_player(char *name, v3f64 *pos_ptr)
+bool database_load_player(char *name, v3f64 *pos, v3f32 *rot)
 {
-       sqlite3_stmt *stmt;
+       sqlite3_stmt *stmt = prepare_statement(players_database, "SELECT pos, rot FROM players WHERE name=?");
 
-       if (! (stmt = prepare_statement(players_database, "SELECT pos FROM players WHERE name=?"))) {
-               fprintf(stderr, "Database error with loading player %s: %s\n", name, sqlite3_errmsg(players_database));
+       if (!stmt) {
+               fprintf(stderr, "[warning] failed loading player %s: %s\n", name, sqlite3_errmsg(players_database));
                return false;
        }
 
@@ -229,49 +237,53 @@ bool database_load_player(char *name, v3f64 *pos_ptr)
        int rc = sqlite3_step(stmt);
        bool found = rc == SQLITE_ROW;
 
-       if (found)
-               v3f64_read(&(Blob) {sqlite3_column_bytes(stmt, 0), (void *) sqlite3_column_blob(stmt, 0)}, pos_ptr);
-       else if (rc != SQLITE_DONE)
-               fprintf(stderr, "Database error with loading player %s: %s\n", name, sqlite3_errmsg(players_database));
+       if (found) {
+               v3f64_read(&(Blob) {sqlite3_column_bytes(stmt, 0), (void *) sqlite3_column_blob(stmt, 0)}, pos);
+               v3f32_read(&(Blob) {sqlite3_column_bytes(stmt, 1), (void *) sqlite3_column_blob(stmt, 1)}, rot);
+       } else if (rc != SQLITE_DONE) {
+               fprintf(stderr, "[warning] failed loading player %s: %s\n", name, sqlite3_errmsg(players_database));
+       }
 
        sqlite3_finalize(stmt);
        return found;
 }
 
 // insert new player into database
-void database_create_player(char *name, v3f64 pos)
+void database_create_player(char *name, v3f64 pos, v3f32 rot)
 {
-       sqlite3_stmt *stmt;
+       sqlite3_stmt *stmt = prepare_statement(players_database, "INSERT INTO players (name, pos, rot) VALUES(?1, ?2, ?3)");
 
-       if (! (stmt = prepare_statement(players_database, "INSERT INTO players (name, pos) VALUES(?1, ?2)"))) {
-               fprintf(stderr, "Database error with creating player %s: %s\n", name, sqlite3_errmsg(players_database));
+       if (!stmt) {
+               fprintf(stderr, "[warning] failed creating player %s: %s\n", name, sqlite3_errmsg(players_database));
                return;
        }
 
        sqlite3_bind_text(stmt, 1, name, strlen(name), SQLITE_TRANSIENT);
        bind_v3f64(stmt, 2, pos);
+       bind_v3f32(stmt, 3, rot);
 
        if (sqlite3_step(stmt) != SQLITE_DONE)
-               fprintf(stderr, "Database error with creating player %s: %s\n", name, sqlite3_errmsg(players_database));
+               fprintf(stderr, "[warning] failed creating player %s: %s\n", name, sqlite3_errmsg(players_database));
 
        sqlite3_finalize(stmt);
 }
 
 // update player position
-void database_update_player_pos(char *name, v3f64 pos)
+void database_update_player_pos_rot(char *name, v3f64 pos, v3f32 rot)
 {
-       sqlite3_stmt *stmt;
+       sqlite3_stmt *stmt = prepare_statement(players_database, "UPDATE players SET pos=?1, rot=?2 WHERE name=?3");
 
-       if (! (stmt = prepare_statement(players_database, "UPDATE players SET pos=?1 WHERE name=?2"))) {
-               fprintf(stderr, "Database error with updating position of player %s: %s\n", name, sqlite3_errmsg(players_database));
+       if (!stmt) {
+               fprintf(stderr, "[warning] failed updating player %s: %s\n", name, sqlite3_errmsg(players_database));
                return;
        }
 
        bind_v3f64(stmt, 1, pos);
-       sqlite3_bind_text(stmt, 2, name, strlen(name), SQLITE_TRANSIENT);
+       bind_v3f32(stmt, 2, rot);
+       sqlite3_bind_text(stmt, 3, name, strlen(name), SQLITE_TRANSIENT);
 
        if (sqlite3_step(stmt) != SQLITE_DONE)
-               fprintf(stderr, "Database error with updating player %s position: %s\n", name, sqlite3_errmsg(players_database));
+               fprintf(stderr, "[warning] failed updating player %s: %s\n", name, sqlite3_errmsg(players_database));
 
        sqlite3_finalize(stmt);
 }
index 40923b78ffc5feace8256441dba587ae1c4fd83d..754b6b9fce726373cd2ae9eef405623c9587b746 100644 (file)
@@ -2,17 +2,17 @@
 #define _DATABASE_H_
 
 #include <stdbool.h>
-#include "map.h"
+#include "terrain.h"
 #include "types.h"
 
-bool database_init();                                     // open and initialize world SQLite3 database
-void database_deinit();                                   // close database
-bool database_load_block(MapBlock *block);                // load a block from map database (initializes state, mgs buffer and data), returns false on failure
-void database_save_block(MapBlock *block);                // save a block to database
-bool database_load_meta(const char *key, s64 *value_ptr); // load a meta entry
-void database_save_meta(const char *key, s64 value);      // save / update a meta entry
-bool database_load_player(char *name, v3f64 *pos_ptr);    // load player data from database
-void database_create_player(char *name, v3f64 pos);       // insert new player into database
-void database_update_player_pos(char *name, v3f64 pos);   // update player position
+bool database_init();                                                  // open and initialize SQLite3 databases
+void database_deinit();                                                // close databases
+bool database_load_chunk(TerrainChunk *chunk);                         // load a chunk from terrain database (initializes state, tgs buffer and data), returns false on failure
+void database_save_chunk(TerrainChunk *chunk);                         // save a chunk to terrain database
+bool database_load_meta(const char *key, s64 *value_ptr);              // load a meta entry
+void database_save_meta(const char *key, s64 value);                   // save / update a meta entry
+bool database_load_player(char *name, v3f64 *pos, v3f32 *rot);         // load player data from database
+void database_create_player(char *name, v3f64 pos, v3f32 rot);         // insert new player into database
+void database_update_player_pos_rot(char *name, v3f64 pos, v3f32 rot); // update player position
 
-#endif
+#endif // _DATABASE_H_
diff --git a/src/server/mapgen.c b/src/server/mapgen.c
deleted file mode 100644 (file)
index 767c19e..0000000
+++ /dev/null
@@ -1,101 +0,0 @@
-#include <math.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include "environment.h"
-#include "perlin.h"
-#include "server/biomes.h"
-#include "server/mapgen.h"
-#include "server/server_map.h"
-#include "server/trees.h"
-#include "util.h"
-
-void mapgen_set_node(v3s32 pos, MapNode node, MapgenStage mgs, List *changed_blocks)
-{
-       MapgenSetNodeArg arg = {
-               .mgs = mgs,
-               .changed_blocks = changed_blocks,
-       };
-
-       map_set_node(server_map.map, pos, node, true, &arg);
-}
-
-// generate a block (does not manage block state or threading)
-void mapgen_generate_block(MapBlock *block, List *changed_blocks)
-{
-       MapBlockExtraData *extra = block->extra;
-
-       v3s32 block_node_pos = {block->pos.x * MAPBLOCK_SIZE, block->pos.y * MAPBLOCK_SIZE, block->pos.z * MAPBLOCK_SIZE};
-
-       char *block_data[BIOME_COUNT] = {NULL};
-       bool preprocessed_block[BIOME_COUNT] = {false};
-
-       for (u8 x = 0; x < MAPBLOCK_SIZE; x++) {
-               s32 pos_x = block_node_pos.x + x;
-
-               for (u8 z = 0; z < MAPBLOCK_SIZE; z++) {
-                       v2s32 pos_horizontal = {pos_x, block_node_pos.z + z};
-
-                       s32 default_height = (pnoise2d(U32(pos_horizontal.x) / 32.0, U32(pos_horizontal.y) / 32.0, 0.45, 5, seed + SO_HEIGHT) * 16.0)
-                               * (pnoise2d(U32(pos_horizontal.x) / 256.0, U32(pos_horizontal.y) / 256.0, 0.45, 5, seed + SO_HILLYNESS) * 0.5 + 0.5)
-                               + 32.0;
-
-                       f64 factor;
-                       Biome biome = get_biome(pos_horizontal, &factor);
-                       BiomeDef *biome_def = &biomes[biome];
-
-                       if (biome_def->block_data_size > 0 && ! block_data[biome])
-                               block_data[biome] = malloc(biome_def->block_data_size);
-
-                       if (biome_def->preprocess_block && ! preprocessed_block[biome]) {
-                               biome_def->preprocess_block(block, changed_blocks, block_data[biome]);
-                               preprocessed_block[biome] = true;
-                       }
-
-                       char row_data[biome_def->row_data_size];
-
-                       if (biome_def->preprocess_row)
-                               biome_def->preprocess_row(pos_horizontal, factor, row_data, block_data[biome]);
-
-                       s32 height = biome_def->height(pos_horizontal, factor, default_height, row_data, block_data[biome]);
-
-                       for (u8 y = 0; y < MAPBLOCK_SIZE; y++) {
-                               v3s32 pos = {pos_horizontal.x, block_node_pos.y + y, pos_horizontal.y};
-
-                               f64 humidity = get_humidity(pos);
-                               f64 temperature = get_temperature(pos);
-
-                               s32 diff = pos.y - height;
-
-                               Node node = biome_def->generate(pos, diff, humidity, temperature, factor, block, changed_blocks, row_data, block_data[biome]);
-
-                               if (biome_def->snow && diff <= 1 && temperature < 0.0 && node == NODE_AIR)
-                                       node = NODE_SNOW;
-
-                               if (diff == 1) {
-                                       for (int i = 0; i < NUM_TREES; i++) {
-                                               TreeDef *def = &tree_definitions[i];
-
-                                               if (def->condition(pos, humidity, temperature, biome, factor, block, row_data, block_data)
-                                                       && noise2d(pos.x, pos.z, 0, seed + def->offset) * 0.5 + 0.5 < def->probability
-                                                       && smooth2d(U32(pos.x) / def->spread, U32(pos.z) / def->spread, 0, seed + def->area_offset) * 0.5 + 0.5 < def->area_probability) {
-                                                       def->generate(pos, changed_blocks);
-                                                       break;
-                                               }
-                                       }
-                               }
-
-                               pthread_mutex_lock(&block->mtx);
-                               if (extra->mgsb.raw.nodes[x][y][z] <= MGS_TERRAIN) {
-                                       block->data[x][y][z] = map_node_create(node, (Blob) {0, NULL});
-                                       extra->mgsb.raw.nodes[x][y][z] = MGS_TERRAIN;
-                               }
-                               pthread_mutex_unlock(&block->mtx);
-                       }
-               }
-       }
-
-       for (Biome i = 0; i < BIOME_COUNT; i++) {
-               if (block_data[i])
-                       free(block_data[i]);
-       }
-}
diff --git a/src/server/mapgen.h b/src/server/mapgen.h
deleted file mode 100644 (file)
index 8e73e72..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-#ifndef _MAPGEN_H_
-#define _MAPGEN_H_
-
-#include "map.h"
-#include "server/server_map.h"
-
-void mapgen_set_node(v3s32 pos, MapNode node, MapgenStage mgs, List *changed_blocks);
-void mapgen_generate_block(MapBlock *block, List *changed_blocks);                    // generate a block (does not manage block state or threading)
-
-#endif
diff --git a/src/server/schematic.c b/src/server/schematic.c
new file mode 100644 (file)
index 0000000..5a754e8
--- /dev/null
@@ -0,0 +1,94 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include "server/schematic.h"
+#include "terrain.h"
+
+void schematic_load(List *schematic, const char *path, SchematicMapping *mappings, size_t num_mappings)
+{
+       list_ini(schematic);
+
+       FILE *file = fopen(path, "r");
+       if (!file) {
+               fprintf(stderr, "[warning] failed to open schematic %s\n", path);
+               return;
+       }
+
+       char *line = NULL;
+       size_t siz = 0;
+       ssize_t length;
+       int count = 0;
+
+       // getline is POSIX 2008, so we can use it
+       while ((length = getline(&line, &siz, file)) > 0) {
+               count++;
+
+               if (*line == '#')
+                       continue;
+
+               SchematicNode *node = malloc(sizeof *node);
+               node->data = (Blob) {0, NULL};
+
+               v3s32 color;
+               if (sscanf(line, "%d %d %d %2x%2x%2x",
+                               &node->pos.x, &node->pos.z, &node->pos.y,
+                               &color.x, &color.y, &color.z) != 6) {
+                       fprintf(stderr, "[warning] syntax error in schematic %s in line %d: %s\n",
+                               path, count, line);
+                       free(node);
+                       continue;
+               }
+
+               SchematicMapping *mapping = NULL;
+               for (size_t i = 0; i < num_mappings; i++)
+                       if (v3s32_equals(color, mappings[i].color)) {
+                               mapping = &mappings[i];
+                               break;
+                       }
+
+               if (!mapping) {
+                       fprintf(stderr, "[warning] color not mapped to node in schematic %s in line %d: %02x%02x%02x\n",
+                               path, count, color.x, color.y, color.z);
+                       free(node);
+                       continue;
+               }
+
+               node->type = mapping->type;
+
+               if (mapping->use_color)
+                       ColorData_write(&node->data, &(ColorData) {{
+                               (f32) color.x / 0xFF,
+                               (f32) color.y / 0xFF,
+                               (f32) color.z / 0xFF,
+                       }});
+
+               list_apd(schematic, node);
+       }
+
+       if (line)
+               free(line);
+
+       fclose(file);
+}
+
+void schematic_place(List *schematic, v3s32 pos, TerrainGenStage tgs, List *changed_chunks)
+{
+       LIST_ITERATE(schematic, list_node) {
+               SchematicNode *node = list_node->dat;
+
+               server_terrain_gen_node(
+                       v3s32_add(pos, node->pos),
+                       terrain_node_create(node->type, node->data),
+                       tgs, changed_chunks);
+       }
+}
+
+static void delete_schematic_node(SchematicNode *node)
+{
+       Blob_free(&node->data);
+       free(node);
+}
+
+void schematic_delete(List *schematic)
+{
+       list_clr(schematic, (void *) &delete_schematic_node, NULL, NULL);
+}
diff --git a/src/server/schematic.h b/src/server/schematic.h
new file mode 100644 (file)
index 0000000..d4e1a2e
--- /dev/null
@@ -0,0 +1,27 @@
+#ifndef _SCHEMATIC_H_
+#define _SCHEMATIC_H_
+
+#include <dragonstd/list.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include "node.h"
+#include "server/server_terrain.h"
+#include "types.h"
+
+typedef struct {
+       v3s32 color;
+       NodeType type;
+       bool use_color;
+} SchematicMapping;
+
+typedef struct {
+       v3s32 pos;
+       NodeType type;
+       Blob data;
+} SchematicNode;
+
+void schematic_load(List *schematic, const char *path, SchematicMapping *mappings, size_t num_mappings);
+void schematic_place(List *schematic, v3s32 pos, TerrainGenStage tgs, List *changed_chunks);
+void schematic_delete(List *schematic);
+
+#endif // _SCHEMATIC_H_
index 1a84a521113e453455a8a037050b66c75020f9df..f4bfaa104918a73642fc179a7d8c7b59b78f0a65 100644 (file)
@@ -1,17 +1,19 @@
+#define _GNU_SOURCE // don't worry, GNU extensions are only used when available
+#include <dragonnet/addr.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <dragonnet/addr.h>
+#include <pthread.h>
 #include "interrupt.h"
 #include "server/database.h"
 #include "server/server.h"
-#include "server/server_map.h"
 #include "server/server_player.h"
-#include "util.h"
+#include "server/server_terrain.h"
 
 DragonnetListener *server;
 
-static bool on_recv(DragonnetPeer *peer, DragonnetTypeId type, unused void *pkt)
+static bool on_recv(DragonnetPeer *peer, DragonnetTypeId type, __attribute__((unused)) void *pkt)
 {
+       // this is recv thread, so we don't need lock_auth
        return ((ServerPlayer *) peer->extra)->auth != (type == DRAGONNET_TYPE_ToServerAuth);
 }
 
@@ -22,71 +24,78 @@ static void on_ToServerAuth(DragonnetPeer *peer, ToServerAuth *pkt)
 }
 
 // set a node on the map
-static void on_ToServerSetnode(unused DragonnetPeer *peer, ToServerSetnode *pkt)
+static void on_ToServerSetnode(__attribute__((unused)) DragonnetPeer *peer, ToServerSetnode *pkt)
 {
-       map_set_node(server_map.map, pkt->pos, map_node_create(pkt->node, (Blob) {0, NULL}), false, NULL);
+       terrain_set_node(server_terrain, pkt->pos,
+               terrain_node_create(pkt->node, (Blob) {0, NULL}),
+               false, NULL);
 }
 
 // update player's position
-static void on_ToServerPos(DragonnetPeer *peer, ToServerPos *pkt)
+static void on_ToServerPosRot(DragonnetPeer *peer, ToServerPosRot *pkt)
 {
        ServerPlayer *player = peer->extra;
 
-       pthread_rwlock_wrlock(&player->pos_lock);
+       pthread_rwlock_wrlock(&player->lock_pos);
        player->pos = pkt->pos;
-       database_update_player_pos(player->name, player->pos);
-       pthread_rwlock_unlock(&player->pos_lock);
+       player->rot = pkt->rot;
+
+       // this is recv thread, no lock_auth needed
+       database_update_player_pos_rot(player->name, player->pos, player->rot);
+       pthread_rwlock_unlock(&player->lock_pos);
 }
 
-// tell server map manager client requested the block
-static void on_ToServerRequestBlock(DragonnetPeer *peer, ToServerRequestBlock *pkt)
+// tell server map manager client requested the chunk
+static void on_ToServerRequestChunk(DragonnetPeer *peer, ToServerRequestChunk *pkt)
 {
-       server_map_requested_block(peer->extra, pkt->pos);
+       server_terrain_requested_chunk(peer->extra, pkt->pos);
 }
 
 // server entry point
 int main(int argc, char **argv)
 {
+#ifdef __GLIBC__ // check whether bloat is enabled
+       pthread_setname_np(pthread_self(), "main");
+#endif // __GLIBC__
+
        if (argc < 2) {
-               fprintf(stderr, "Missing address\n");
+               fprintf(stderr, "[error] missing address\n");
                return EXIT_FAILURE;
        }
 
-       if (! (server = dragonnet_listener_new(argv[1]))) {
-               fprintf(stderr, "Failed to listen to connections\n");
+       if (!(server = dragonnet_listener_new(argv[1]))) {
+               fprintf(stderr, "[error] failed to listen to connections\n");
                return EXIT_FAILURE;
        }
 
        char *address = dragonnet_addr_str(server->laddr);
-       printf("Listening on %s\n", address);
+       printf("[info] listening on %s\n", address);
        free(address);
 
        server->on_connect = &server_player_add;
        server->on_disconnect = &server_player_remove;
        server->on_recv = &on_recv;
-       server->on_recv_type[DRAGONNET_TYPE_ToServerAuth] =         (void *) &on_ToServerAuth;
-       server->on_recv_type[DRAGONNET_TYPE_ToServerSetnode] =      (void *) &on_ToServerSetnode;
-       server->on_recv_type[DRAGONNET_TYPE_ToServerPos] =          (void *) &on_ToServerPos;
-       server->on_recv_type[DRAGONNET_TYPE_ToServerRequestBlock] = (void *) &on_ToServerRequestBlock;
+       server->on_recv_type[DRAGONNET_TYPE_ToServerAuth        ] = (void *) &on_ToServerAuth;
+       server->on_recv_type[DRAGONNET_TYPE_ToServerSetnode     ] = (void *) &on_ToServerSetnode;
+       server->on_recv_type[DRAGONNET_TYPE_ToServerPosRot      ] = (void *) &on_ToServerPosRot;
+       server->on_recv_type[DRAGONNET_TYPE_ToServerRequestChunk] = (void *) &on_ToServerRequestChunk;
 
        interrupt_init();
-
-       if (! database_init())
+       if (!database_init())
                return EXIT_FAILURE;
-
-       server_map_init();
+       server_terrain_init();
        server_player_init();
 
-       server_map_prepare_spawn();
+       server_terrain_prepare_spawn();
        dragonnet_listener_run(server);
 
-       flag_wait(interrupt);
+       flag_slp(&interrupt);
 
-       printf("Shutting down\n");
+       printf("[info] shutting down\n");
        dragonnet_listener_close(server);
 
        server_player_deinit();
-       server_map_deinit();
+       server_terrain_deinit();
        database_deinit();
        interrupt_deinit();
 
index c2adddb66adeb9dcb440d040fee8ba34c872e441..67de00b49962ab2ca15764b54f38b661d69804bd 100644 (file)
@@ -5,4 +5,4 @@
 
 DragonnetListener *server;
 
-#endif
+#endif // _SERVER_H_
index e4d146ee871a1828103020fd4736cdcae3d38abe..51a39c28e209ce44d6bfd263bd1bab7dc0b7b681 100644 (file)
@@ -2,23 +2,56 @@
 #include "server/server_config.h"
 
 struct ServerConfig server_config = {
-       .simulation_distance = 10,
-       .mapgen_threads = 4,
+       .load_distance = 10,
+       .terrain_gen_threads = 4,
+       .movement = {
+               .speed_normal = 4.317,
+               .speed_flight = 25.0,
+               .gravity = 32.0,
+               .jump = 8.944,
+       }
+};
+
+#define NUM_CONFIG_ENTRIES 6
+static ConfigEntry config_entries[NUM_CONFIG_ENTRIES] = {
+       {
+               .type = CONFIG_UINT,
+               .key = "load_distance",
+               .value = &server_config.load_distance,
+       },
+       {
+               .type = CONFIG_UINT,
+               .key = "terrain_gen_threads",
+               .value = &server_config.terrain_gen_threads,
+       },
+       {
+               .type = CONFIG_FLOAT,
+               .key = "movement.speed_normal",
+               .value = &server_config.movement.speed_normal,
+       },
+       {
+               .type = CONFIG_FLOAT,
+               .key = "movement.speed_flight",
+               .value = &server_config.movement.speed_flight,
+       },
+       {
+               .type = CONFIG_FLOAT,
+               .key = "movement.gravity",
+               .value = &server_config.movement.gravity,
+       },
+       {
+               .type = CONFIG_FLOAT,
+               .key = "movement.jump",
+               .value = &server_config.movement.jump,
+       },
 };
 
 __attribute__((constructor)) static void server_config_init()
 {
-       config_read("server.conf", (ConfigEntry[]) {
-               {
-                       .type = CT_UINT,
-                       .key = "simulation_distance",
-                       .value = &server_config.simulation_distance,
-               },
-               {
-                       .type = CT_UINT,
-                       .key = "mapgen_threads",
-                       .value = &server_config.mapgen_threads,
-               },
-       }, 2);
+       config_read("server.conf", config_entries, NUM_CONFIG_ENTRIES);
 }
 
+__attribute__((destructor)) static void server_config_deinit()
+{
+       config_free(config_entries, NUM_CONFIG_ENTRIES);
+}
index 4effe332b164a086fb38de8b585116c0c16a1ce2..6355aa2e96b6ea848d7ad1d7bd3f3279fcbae778 100644 (file)
@@ -2,8 +2,16 @@
 #define _SERVER_CONFIG_H_
 
 extern struct ServerConfig {
-       unsigned int simulation_distance;
-       unsigned int mapgen_threads;
+       unsigned int load_distance;
+       unsigned int terrain_gen_threads;
+       struct {
+               double speed_normal;
+               double speed_flight;
+               double gravity;
+               double jump;
+
+               // allow_op, allow_all, force_on, force_off
+       } movement;
 } server_config;
 
-#endif
+#endif // _SERVER_CONFIG_H_
diff --git a/src/server/server_map.c b/src/server/server_map.c
deleted file mode 100644 (file)
index 854afe1..0000000
+++ /dev/null
@@ -1,399 +0,0 @@
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <stdio.h>
-#include "interrupt.h"
-#include "map.h"
-#include "server/database.h"
-#include "server/mapgen.h"
-#include "server/server_config.h"
-#include "server/server_map.h"
-#include "util.h"
-
-// this file is too long
-struct ServerMap server_map;
-
-// utility functions
-
-// return true if a player is close enough to a block to access it
-static bool within_simulation_distance(ServerPlayer *player, v3s32 blkp, u32 dist)
-{
-       pthread_rwlock_rdlock(&player->pos_lock);
-       v3s32 ppos = map_node_to_block_pos((v3s32) {player->pos.x, player->pos.y, player->pos.z}, NULL);
-       pthread_rwlock_unlock(&player->pos_lock);
-
-       return abs(ppos.x - blkp.x) <= (s32) dist
-               && abs(ppos.y - blkp.y) <= (s32) dist
-               && abs(ppos.z - blkp.z) <= (s32) dist;
-}
-
-// send a block to a client and reset block request
-static void send_block(ServerPlayer *player, MapBlock *block)
-{
-       if (! within_simulation_distance(player, block->pos, server_config.simulation_distance))
-               return;
-
-       dragonnet_peer_send_ToClientBlock(player->peer, &(ToClientBlock) {
-               .pos = block->pos,
-               .data = ((MapBlockExtraData *) block->extra)->data,
-       });
-}
-
-// send block to near clients
-// block mutex has to be locked
-static void send_block_to_near(MapBlock *block)
-{
-       MapBlockExtraData *extra = block->extra;
-
-       if (extra->state == MBS_GENERATING)
-               return;
-
-       Blob_free(&extra->data);
-       extra->data = map_serialize_block(block);
-
-       database_save_block(block);
-
-       if (extra->state == MBS_CREATED)
-               return;
-
-       server_player_iterate((void *) &send_block, block);
-}
-
-// list_clear_func callback for sending changed blocks to near clients
-static void list_send_block(void *key, unused void *value, unused void *arg)
-{
-       MapBlock *block = key;
-
-       pthread_mutex_lock(&block->mtx);
-       send_block_to_near(block);
-       pthread_mutex_unlock(&block->mtx);
-}
-
-// me when the
-static void mapgen_step()
-{
-       MapBlock *block = queue_dequeue(server_map.mapgen_tasks);
-
-       if (! block)
-               return;
-
-       MapBlockExtraData *extra = block->extra;
-
-       List changed_blocks = list_create(NULL);
-       list_put(&changed_blocks, block, NULL);
-
-       mapgen_generate_block(block, &changed_blocks);
-
-       pthread_mutex_lock(&block->mtx);
-       extra->state = MBS_READY;
-       pthread_mutex_unlock(&block->mtx);
-
-       list_clear_func(&changed_blocks, &list_send_block, NULL);
-
-       pthread_mutex_lock(&server_map.num_blocks_mtx);
-       server_map.num_blocks--;
-       pthread_mutex_unlock(&server_map.num_blocks_mtx);
-}
-
-// there was a time when i wrote actually useful comments lol
-static void *mapgen_thread(unused void *arg)
-{
-       while (! server_map.cancel)
-               mapgen_step();
-
-       return NULL;
-}
-
-// enqueue block
-static void generate_block(MapBlock *block)
-{
-       if (server_map.cancel)
-               return;
-
-       pthread_mutex_lock(&server_map.num_blocks_mtx);
-       server_map.num_blocks++;
-       pthread_mutex_unlock(&server_map.num_blocks_mtx);
-
-       MapBlockExtraData *extra = block->extra;
-       extra->state = MBS_GENERATING;
-       queue_enqueue(server_map.mapgen_tasks, block);
-}
-
-// map callbacks
-// note: all these functions require the block mutex to be locked, which is always the case when a map callback is invoked
-
-// callback for initializing a newly created block
-// load block from database or initialize state, mgstage buffer and data
-static void on_create_block(MapBlock *block)
-{
-       MapBlockExtraData *extra = block->extra = malloc(sizeof(MapBlockExtraData));
-
-       if (! database_load_block(block)) {
-               extra->state = MBS_CREATED;
-               extra->data = (Blob) {0, NULL};
-
-               ITERATE_MAPBLOCK {
-                       block->data[x][y][z] = map_node_create(NODE_AIR, (Blob) {0, NULL});
-                       extra->mgsb.raw.nodes[x][y][z] = MGS_VOID;
-               }
-       }
-}
-
-// callback for deleting a block
-// free extra data
-static void on_delete_block(MapBlock *block)
-{
-       MapBlockExtraData *extra = block->extra;
-
-       Blob_free(&extra->data);
-       free(extra);
-}
-
-// callback for determining whether a block should be returned by map_get_block
-// hold back blocks that are not fully generated except when the create flag is set to true
-static bool on_get_block(MapBlock *block, bool create)
-{
-       MapBlockExtraData *extra = block->extra;
-
-       if (extra->state < MBS_READY && ! create)
-               return false;
-
-       return true;
-}
-
-// callback for deciding whether a set_node call succeeds or not
-// reject set_node calls that try to override nodes placed by later mapgen stages, else update mgs buffer - also make sure block is inserted into changed blocks list
-static bool on_set_node(MapBlock *block, v3u8 offset, unused MapNode *node, void *arg)
-{
-       MapgenSetNodeArg *msn_arg = arg;
-
-       MapgenStage mgs;
-
-       if (msn_arg)
-               mgs = msn_arg->mgs;
-       else
-               mgs = MGS_PLAYER;
-
-       MapgenStage *old_mgs = &((MapBlockExtraData *) block->extra)->mgsb.raw.nodes[offset.x][offset.y][offset.z];
-
-       if (mgs >= *old_mgs) {
-               *old_mgs = mgs;
-
-               if (msn_arg)
-                       list_put(msn_arg->changed_blocks, block, NULL);
-
-               return true;
-       }
-
-       return false;
-}
-
-// callback for when a block changes
-// send block to near clients if not part of map generation
-static void on_after_set_node(MapBlock *block, unused v3u8 offset, void *arg)
-{
-       if (! arg)
-               send_block_to_near(block);
-}
-
-// generate a hut for new players to spawn in
-static void generate_spawn_hut()
-{
-       Blob wood_color = {0, NULL};
-       HSLData_write(&wood_color, &(HSLData) {{0.11f, 1.0f, 0.29f}});
-
-       List changed_blocks = list_create(NULL);
-
-       for (s32 x = -4; x <= +4; x++) {
-               for (s32 y = 0; y <= 3; y++) {
-                       for (s32 z = -3; z <= +2; z++) {
-                               mapgen_set_node((v3s32) {x, server_map.spawn_height + y, z}, map_node_create(NODE_AIR, (Blob) {0, NULL}), MGS_PLAYER, &changed_blocks);
-                       }
-               }
-       }
-
-       for (s32 x = -5; x <= +5; x++) {
-               for (s32 z = -4; z <= +3; z++) {
-                       mapgen_set_node((v3s32) {x, server_map.spawn_height - 1, z}, map_node_create(NODE_OAK_WOOD, wood_color), MGS_PLAYER, &changed_blocks);
-                       mapgen_set_node((v3s32) {x, server_map.spawn_height + 4, z}, map_node_create(NODE_OAK_WOOD, wood_color), MGS_PLAYER, &changed_blocks);
-               }
-       }
-
-       for (s32 y = 0; y <= 3; y++) {
-               for (s32 x = -5; x <= +5; x++) {
-                       mapgen_set_node((v3s32) {x, server_map.spawn_height + y, -4}, map_node_create(((y == 1 || y == 2) && ((x >= -3 && x <= -1) || (x >= +1 && x <= +2))) ? NODE_AIR : NODE_OAK_WOOD, wood_color), MGS_PLAYER, &changed_blocks);
-                       mapgen_set_node((v3s32) {x, server_map.spawn_height + y, +3}, map_node_create(((y == 1 || y == 2) && ((x >= -3 && x <= -2) || (x >= +1 && x <= +3))) ? NODE_AIR : NODE_OAK_WOOD, wood_color), MGS_PLAYER, &changed_blocks);
-               }
-       }
-
-       for (s32 y = 0; y <= 3; y++) {
-               for (s32 z = -3; z <= +2; z++) {
-                       mapgen_set_node((v3s32) {-5, server_map.spawn_height + y, z}, map_node_create(NODE_OAK_WOOD, wood_color), MGS_PLAYER, &changed_blocks);
-                       mapgen_set_node((v3s32) {+5, server_map.spawn_height + y, z}, map_node_create(((y != 3) && (z == -1 || z == +0)) ? NODE_AIR : NODE_OAK_WOOD, wood_color), MGS_PLAYER, &changed_blocks);
-               }
-       }
-
-       v2s32 posts[6] = {
-               {-4, -3},
-               {-4, +2},
-               {+4, -3},
-               {+4, +2},
-               {+5, -1},
-               {+5, +0},
-       };
-
-       for (int i = 0; i < 6; i++) {
-               for (s32 y = server_map.spawn_height - 2;; y--) {
-                       v3s32 pos = {posts[i].x, y, posts[i].y};
-                       Node node = map_get_node(server_map.map, pos).type;
-
-                       if (i >= 4) {
-                               if (node != NODE_AIR)
-                                       break;
-
-                               pos.y++;
-                       }
-
-                       if (node_definitions[node].solid)
-                               break;
-
-                       mapgen_set_node(pos, map_node_create(node == NODE_LAVA ? NODE_VULCANO_STONE : NODE_OAK_WOOD, wood_color), MGS_PLAYER, &changed_blocks);
-               }
-       }
-
-       list_clear_func(&changed_blocks, &list_send_block, NULL);
-}
-
-// public functions
-
-// ServerMap singleton constructor
-void server_map_init()
-{
-       server_map.map = map_create((MapCallbacks) {
-               .create_block = &on_create_block,
-               .delete_block = &on_delete_block,
-               .get_block = &on_get_block,
-               .set_node = &on_set_node,
-               .after_set_node = &on_after_set_node,
-       });
-
-       server_map.cancel = false;
-       server_map.mapgen_tasks = queue_create();
-       server_map.mapgen_threads = malloc(sizeof *server_map.mapgen_threads * server_config.mapgen_threads);
-       server_map.num_blocks = 0;
-       pthread_mutex_init(&server_map.num_blocks_mtx, NULL);
-
-       for (unsigned int i = 0; i < server_config.mapgen_threads; i++)
-               pthread_create(&server_map.mapgen_threads[i], NULL, &mapgen_thread, NULL);
-}
-
-// ServerMap singleton destructor
-void server_map_deinit()
-{
-       queue_finish(server_map.mapgen_tasks);
-       server_map.cancel = true;
-       queue_cancel(server_map.mapgen_tasks);
-
-       for (unsigned int i = 0; i < server_config.mapgen_threads; i++)
-               pthread_join(server_map.mapgen_threads[i], NULL);
-       free(server_map.mapgen_threads);
-
-       pthread_mutex_destroy(&server_map.num_blocks_mtx);
-       queue_delete(server_map.mapgen_tasks);
-       map_delete(server_map.map);
-}
-
-// handle block request from client (thread safe)
-void server_map_requested_block(ServerPlayer *player, v3s32 pos)
-{
-       if (within_simulation_distance(player, pos, server_config.simulation_distance)) {
-               MapBlock *block = map_get_block(server_map.map, pos, true);
-
-               pthread_mutex_lock(&block->mtx);
-
-               MapBlockExtraData *extra = block->extra;
-               switch (extra->state) {
-                       case MBS_CREATED:
-                               generate_block(block);
-                               break;
-
-                       case MBS_GENERATING:
-                               break;
-
-                       case MBS_READY:
-                               send_block(player, block);
-               };
-
-               pthread_mutex_unlock(&block->mtx);
-       }
-}
-
-static void update_percentage()
-{
-       static s32 total = 3 * 3 * 21;
-       static s32 done = -1;
-       static s32 last_percentage = -1;
-
-       if (done < total)
-               done++;
-
-       pthread_mutex_lock(&server_map.num_blocks_mtx);
-       s32 percentage = 100.0 * (done - server_map.num_blocks) / total;
-       pthread_mutex_unlock(&server_map.num_blocks_mtx);
-
-       if (percentage > last_percentage) {
-               last_percentage = percentage;
-               printf("Preparing spawn... %d%%\n", percentage);
-       }
-
-}
-
-// prepare spawn region
-void server_map_prepare_spawn()
-{
-       update_percentage();
-
-       for (s32 x = -1; x <= (s32) 1; x++) {
-               for (s32 y = -10; y <= (s32) 10; y++) {
-                       for (s32 z = -1; z <= (s32) 1; z++) {
-                               if (interrupt->done)
-                                       return;
-
-                               MapBlock *block = map_get_block(server_map.map, (v3s32) {x, y, z}, true);
-
-                               pthread_mutex_lock(&block->mtx);
-                               if (((MapBlockExtraData *) block->extra)->state == MBS_CREATED)
-                                       generate_block(block);
-                               pthread_mutex_unlock(&block->mtx);
-
-                               update_percentage();
-                       }
-               }
-       }
-
-       while (true) {
-               pthread_mutex_lock(&server_map.num_blocks_mtx);
-               bool done = (server_map.num_blocks == 0);
-               pthread_mutex_unlock(&server_map.num_blocks_mtx);
-
-               if (done)
-                       break;
-
-               update_percentage();
-               sched_yield();
-       }
-
-       s64 saved_spawn_height;
-       if (database_load_meta("spawn_height", &saved_spawn_height)) {
-               server_map.spawn_height = saved_spawn_height;
-       } else {
-               s32 spawn_height = -1;
-
-               while (map_get_node(server_map.map, (v3s32) {0, ++spawn_height, 0}).type != NODE_AIR)
-                       ;
-
-               server_map.spawn_height = spawn_height + 5;
-               generate_spawn_hut();
-               database_save_meta("spawn_height", server_map.spawn_height);
-       }
-}
diff --git a/src/server/server_map.h b/src/server/server_map.h
deleted file mode 100644 (file)
index 68f3a3e..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-#ifndef _SERVER_MAP_H_
-#define _SERVER_MAP_H_
-
-#include <stdatomic.h>
-#include <stdbool.h>
-#include <stddef.h>
-#include <pthread.h>
-#include <dragonstd/queue.h>
-#include "map.h"
-#include "server/server_player.h"
-#include "types.h"
-
-typedef enum
-{
-       MBS_CREATED,    // block exists but was not yet generated
-       MBS_GENERATING, // currently generating in a seperate thread
-       MBS_READY,      // generation finished
-} MapBlockState;
-
-typedef enum
-{
-       MGS_VOID,     // initial air, can be overridden by anything
-       MGS_TERRAIN,  // basic terrain, can be overridden by anything except the void
-       MGS_BOULDERS, // boulders, replace terrain
-       MGS_TREES,    // trees replace boulders
-       MGS_PLAYER,   // player-placed nodes or things placed after map generation
-} MapgenStage;
-
-typedef struct {
-       MapgenStage mgs;
-       List *changed_blocks;
-} MapgenSetNodeArg;
-
-typedef struct
-{
-       Blob data;                    // the big cum
-       MapBlockState state;          // generation state of the block
-       pthread_t mapgen_thread;      // thread that is generating block
-       MapgenStageBuffer mgsb;       // buffer to make sure mapgen only overrides things it should
-} MapBlockExtraData;
-
-extern struct ServerMap {
-       atomic_bool cancel;             // remove the smooth
-       Map *map;                       // map object, data is stored here
-       Queue *mapgen_tasks;            // this is terry the fat shark
-       pthread_t *mapgen_threads;      // thread pool
-       s32 spawn_height;               // elevation to spawn players at
-       unsigned int num_blocks;        // number of enqueued / generating blocks
-       pthread_mutex_t num_blocks_mtx; // lock to protect the above
-} server_map; // ServerMap singleton
-
-void server_map_init();                                           // ServerMap singleton constructor
-void server_map_deinit();                                         // ServerMap singleton destructor
-void server_map_requested_block(ServerPlayer *player, v3s32 pos); // handle block request from client (thread safe)
-void server_map_prepare_spawn();                                  // prepare spawn region
-
-#endif
index e48019615568ed9efbb2567b18ab801d10b66d7a..1f464f93e394a374246b9171d9a122980ecf4ed1 100644 (file)
+#include <dragonstd/map.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
 #include <pthread.h>
-#include <dragonstd/list.h>
+#include "day.h"
+#include "entity.h"
+#include "perlin.h"
 #include "server/database.h"
 #include "server/server_config.h"
-#include "server/server_map.h"
 #include "server/server_player.h"
-#include "perlin.h"
-#include "day.h"
-#include "util.h"
-
-static bool shutting_down = false;
-static pthread_rwlock_t shutting_down_lock;
+#include "server/server_terrain.h"
 
-static List players;
-static pthread_rwlock_t players_lock;
+static Map players;
+static Map players_named;
 
-static List names;
-static pthread_rwlock_t names_lock;
+// main thread
+// called on server shutdown
+static void player_drop(ServerPlayer *player)
+{
+       pthread_rwlock_rdlock(&player->lock_peer);
+       pthread_t recv_thread = player->peer ? player->peer->recv_thread : 0;
+       pthread_rwlock_unlock(&player->lock_peer);
 
-static u64 next_id = 1;
+       server_player_disconnect(player);
+       if (recv_thread)
+               pthread_join(recv_thread, NULL);
 
-static bool list_compare_u64(u64 *p1, u64 *p2)
-{
-       return *p1 == *p2;
+       refcount_drp(&player->rc); // map no longer has a reference to player
 }
 
-static bool get_lock(pthread_rwlock_t *lock, bool write)
+// any thread
+// called when all refs have been dropped
+static void player_delete(ServerPlayer *player)
 {
-       pthread_rwlock_rdlock(&shutting_down_lock);
-       if (shutting_down) {
-               pthread_rwlock_unlock(&shutting_down_lock);
-               return false;
-       }
+       refcount_dst(&player->rc);
 
-       if (write)
-               pthread_rwlock_wrlock(lock);
-       else
-               pthread_rwlock_rdlock(lock);
+       pthread_rwlock_destroy(&player->lock_peer);
 
-       pthread_rwlock_unlock(&shutting_down_lock);
-       return true;
+       free(player->name);
+       pthread_rwlock_destroy(&player->lock_auth);
+
+       pthread_rwlock_destroy(&player->lock_pos);
+
+       free(player);
 }
 
-void server_player_init()
+// recv thread
+// called when auth was successful
+static void player_spawn(ServerPlayer *player)
 {
-       pthread_rwlock_init(&shutting_down_lock, NULL);
+       // lock_pos has already been wrlocked by caller
+       if (!database_load_player(player->name, &player->pos, &player->rot)) {
+               player->pos = (v3f64) {0.0, server_terrain_spawn_height() + 0.5, 0.0};
+               player->rot = (v3f32) {0.0f, 0.0f, 0.0f};
+               database_create_player(player->name, player->pos, player->rot);
+       }
 
-       players = list_create((void *) &list_compare_u64);
-       pthread_rwlock_init(&players_lock, NULL);
+       // since this is recv thread, we don't need lock_peer
+       dragonnet_peer_send_ToClientInfo(player->peer, &(ToClientInfo) {
+               .seed = seed,
+               .load_distance = server_config.load_distance,
+       });
+       dragonnet_peer_send_ToClientTimeOfDay(player->peer, &(ToClientTimeOfDay) {
+               .time_of_day = get_time_of_day(),
+       });
+       dragonnet_peer_send_ToClientMovement(player->peer, &(ToClientMovement) {
+               .flight = false,
+               .collision = true,
+               .speed = server_config.movement.speed_normal,
+               .gravity = server_config.movement.gravity,
+               .jump = server_config.movement.jump,
+       });
+       dragonnet_peer_send_ToClientEntityAdd(player->peer, &(ToClientEntityAdd) {
+               .type = ENTITY_LOCALPLAYER,
+               .data = {
+                       .id = player->id,
+                       .pos = player->pos,
+                       .rot = player->rot,
+                       .box = {{-0.3f, 0.0f, -0.3f}, {0.3f, 1.75f, 0.3f}},
+                       .eye = {0.0f, 1.75f, 0.0f},
+                       .nametag = NULL,
+               },
+       });
+}
 
-       names = list_create(&list_compare_string);
-       pthread_rwlock_init(&names_lock, NULL);
+// any thread
+// called when adding, getting or removing a player from the map
+static int cmp_player_id(const Refcount *player, const u64 *id)
+{
+       return u64_cmp(&((ServerPlayer *) player->obj)->id, id);
 }
 
-// list_clear_func callback used on server shutdown to disconnect all clients properly
-static void list_disconnect_player(void *key, unused void *value, unused void *arg)
+// any thread
+// called when adding, getting or removing a player from the players_named Map
+static int cmp_player_name(const Refcount *player, const char *name)
 {
-       ServerPlayer *player = key;
+       // names of players in players_named Map don't change, no lock_auth needed
+       return strcmp(((ServerPlayer *) player->obj)->name, name);
+}
 
-       pthread_t recv_thread = player->peer->recv_thread;
-       server_player_disconnect(player);
-       pthread_join(recv_thread, NULL);
+// main thread
+// called on server startup
+void server_player_init()
+{
+       map_ini(&players);
+       map_ini(&players_named);
 }
 
+// main thread
+// called on server shutdown
 void server_player_deinit()
 {
-       pthread_rwlock_wrlock(&shutting_down_lock);
-       shutting_down = true;
-
-       pthread_rwlock_wrlock(&players_lock);
-       pthread_rwlock_wrlock(&names_lock);
-       pthread_rwlock_unlock(&shutting_down_lock);
-
-       list_clear_func(&players, &list_disconnect_player, NULL);
-       list_clear(&names);
-
-       pthread_rwlock_destroy(&players_lock);
-       pthread_rwlock_destroy(&names_lock);
-       pthread_rwlock_destroy(&shutting_down_lock);
+       // just forget about name -> player mapping
+       map_cnl(&players_named, &refcount_drp, NULL, NULL,          0);
+       // disconnect players and forget about them
+       map_cnl(&players,       &player_drop,  NULL, &refcount_obj, 0);
 }
 
+// accept thread
+// called on new connection
 void server_player_add(DragonnetPeer *peer)
 {
        ServerPlayer *player = malloc(sizeof *player);
 
-       player->id = next_id++;
+       // ID is allocated later in this function
+       player->id = 0;
+       refcount_ini(&player->rc, player, &player_delete);
+
        player->peer = peer;
-       pthread_rwlock_init(&player->ref, NULL);
+       pthread_rwlock_init(&player->lock_peer, NULL);
+
        player->auth = false;
+       // use address as name until auth is done
        player->name = dragonnet_addr_str(peer->raddr);
-       pthread_rwlock_init(&player->auth_lock, NULL);
-       player->pos = (v3f64) {0.0f, 0.0f, 0.0f};
-       pthread_rwlock_init(&player->pos_lock, NULL);
+       pthread_rwlock_init(&player->lock_auth, NULL);
 
-       printf("Connected %s\n", player->name);
+       player->pos = (v3f64) {0.0f, 0.0f, 0.0f};
+       player->rot = (v3f32) {0.0f, 0.0f, 0.0f};
+       pthread_rwlock_init(&player->lock_pos, NULL);
 
-       // accept thread is joined before shutdown, we are guaranteed to obtain the lock
-       pthread_rwlock_wrlock(&players_lock);
+       printf("[access] connected %s\n", player->name);
+       peer->extra = refcount_grb(&player->rc);
 
-       list_put(&players, &player->id, player);
-       peer->extra = player;
+       // keep the search tree somewhat balanced by using random IDs
+       // duplicate IDs are very unlikely, but it doesn't hurt to check
+       // make sure to avoid 0 since it's not a valid ID
+       while (!player->id || !map_add(&players, &player->id, &player->rc, &cmp_player_id, &refcount_inc))
+               player->id = random();
 
-       pthread_rwlock_unlock(&players_lock);
+       // player has been grabbed by Map and peer
+       refcount_drp(&player->rc);
 }
 
+// recv thread
+// called on connection close
 void server_player_remove(DragonnetPeer *peer)
 {
        ServerPlayer *player = peer->extra;
-
-       // only (this) recv thread will modify the auth or name fields, no rdlocks needed
-
-       if (get_lock(&players_lock, true)) {
-               list_delete(&players, &player->id);
-               pthread_rwlock_unlock(&players_lock);
-
-               printf("Disconnected %s\n", player->name);
-       }
-
-       if (player->auth && get_lock(&names_lock, true)) {
-               list_delete(&names, player->name);
-               pthread_rwlock_unlock(&names_lock);
-       }
-
-       pthread_rwlock_wrlock(&player->ref);
-
-       free(player->name);
-
-       pthread_rwlock_destroy(&player->ref);
-       pthread_rwlock_destroy(&player->auth_lock);
-       pthread_rwlock_destroy(&player->pos_lock);
-
-       free(player);
-}
-
-u64 server_player_find(char *name)
-{
-       if (! get_lock(&names_lock, false))
-               return 0;
-
-       u64 *id = list_get(&names, name);
-       return id ? *id : 0;
+       peer->extra = NULL; // technically not necessary, but just in case
+
+       // peer will be deleted - forget about it!
+       pthread_rwlock_wrlock(&player->lock_peer);
+       player->peer = NULL;
+       pthread_rwlock_unlock(&player->lock_peer);
+
+       // only (this) recv thread will modify the auth or name fields, no lock_auth needed
+       // map_del returns false if it was canceled
+       // (we don't want disconnect messages for every player on server shutdown)
+       if (map_del(&players, &player->id, &cmp_player_id, &refcount_drp, NULL, NULL))
+               printf("[access] disconnected %s\n", player->name);
+       if (player->auth)
+               map_del(&players_named, player->name, &cmp_player_name, &refcount_drp, NULL, NULL);
+
+       // peer no longer has a reference to player
+       refcount_drp(&player->rc);
 }
 
+// any thread
 ServerPlayer *server_player_grab(u64 id)
 {
-       if (! id)
-               return NULL;
-
-       if (! get_lock(&players_lock, false))
-               return NULL;
-
-       ServerPlayer *player = list_get(&players, &id);
-       if (player)
-               pthread_rwlock_rdlock(&player->ref);
-
-       pthread_rwlock_unlock(&players_lock);
-
-       return player;
+       // 0 is an invalid ID
+       return id ? map_get(&players, &id, &cmp_player_id, &refcount_grb) : NULL;
 }
 
-void server_player_drop(ServerPlayer *player)
+// any thread
+ServerPlayer *server_player_grab_named(char *name)
 {
-       pthread_rwlock_unlock(&player->ref);
+       // NULL is an invalid name
+       return name ? map_get(&players, name, &cmp_player_name, &refcount_grb) : NULL;
 }
 
+// recv thread
+// called on recv auth packet
 bool server_player_auth(ServerPlayer *player, char *name)
 {
-       if (! get_lock(&names_lock, true))
-               return false;
+       pthread_rwlock_wrlock(&player->lock_auth);
+       pthread_rwlock_wrlock(&player->lock_pos);
+
+       // temporary change name, save old name to either free or reset it if auth fails
+       char *old_name = player->name;
+       player->name = name;
 
-       pthread_rwlock_wrlock(&player->auth_lock);
-       pthread_rwlock_wrlock(&player->pos_lock);
+       bool success = map_add(&players_named, player->name, &player->rc, &cmp_player_name, &refcount_inc);
 
-       bool success = list_put(&names, name, &player->id);
+       printf("[access] authentication %s: %s -> %s\n", success ? "success" : "failure", old_name, player->name);
 
+       // since this is recv thread, we don't need lock_peer
        dragonnet_peer_send_ToClientAuth(player->peer, &(ToClientAuth) {
                .success = success,
        });
 
        if (success) {
-               printf("Authentication %s: %s -> %s\n", success ? "success" : "failure", player->name, name);
-
-               free(player->name);
-               player->name = name;
+               free(old_name);
                player->auth = true;
-
-               if (! database_load_player(player->name, &player->pos)) {
-                       player->pos = (v3f64) {0.0, server_map.spawn_height + 0.5, 0.0};
-                       database_create_player(player->name, player->pos);
-               }
-
-               dragonnet_peer_send_ToClientInfo(player->peer, &(ToClientInfo) {
-                       .seed = seed,
-                       .simulation_distance = server_config.simulation_distance,
-               });
-
-               dragonnet_peer_send_ToClientTimeOfDay(player->peer, &(ToClientTimeOfDay) {
-                       .time_of_day = get_time_of_day(),
-               });
-
-               server_player_send_pos(player);
+               // load player from database and send some initial info
+               player_spawn(player);
+       } else {
+               player->name = old_name;
        }
 
-       pthread_rwlock_unlock(&player->pos_lock);
-       pthread_rwlock_unlock(&player->auth_lock);
-       pthread_rwlock_unlock(&names_lock);
-
+       pthread_rwlock_unlock(&player->lock_pos);
+       pthread_rwlock_unlock(&player->lock_auth);
        return success;
 }
 
+// any thread
 void server_player_disconnect(ServerPlayer *player)
 {
-       dragonnet_peer_shutdown(player->peer);
+       pthread_rwlock_rdlock(&player->lock_peer);
+       // the recv thread will call server_player_remove when the connection was shut down
+       if (player->peer)
+               dragonnet_peer_shutdown(player->peer);
+       pthread_rwlock_unlock(&player->lock_peer);
 }
 
-void server_player_send_pos(ServerPlayer *player)
+// any thread
+void server_player_iterate(void *func, void *arg)
 {
-       dragonnet_peer_send_ToClientPos(player->peer, & (ToClientPos) {
-               .pos = player->pos,
-       });
-}
-
-void server_player_iterate(void (cb)(ServerPlayer *, void *), void *arg)
-{
-       if (! get_lock(&players_lock, false))
-               return;
-
-       ITERATE_LIST(&players, pair) {
-               ServerPlayer *player = pair->value;
-
-               pthread_rwlock_rdlock(&player->auth_lock);
-               if (player->auth)
-                       cb(player, arg);
-               pthread_rwlock_unlock(&player->auth_lock);
-       }
-
-       pthread_rwlock_unlock(&players_lock);
+       map_trv(&players_named, func, arg, &refcount_obj, TRAVERSION_INORDER);
 }
 
 /*
index ad035fcd43138ec6e8d77e7a6f7f996f1bf69a4e..71d0916bebbafaa4b18856096c33ed63988efd9f 100644 (file)
@@ -1,23 +1,26 @@
 #ifndef _SERVER_PLAYER_H_
 #define _SERVER_PLAYER_H_
 
+#include <dragonnet/peer.h>
+#include <dragonstd/refcount.h>
 #include <pthread.h>
 #include <stdbool.h>
-#include <dragonnet/peer.h>
 #include "types.h"
 
-typedef struct
-{
-       u64 id;                     // unique identifier
-       DragonnetPeer *peer;
-       pthread_rwlock_t ref;       // programming socks make you 100% cuter
+typedef struct {
+       u64 id;                        // unique identifier
+       Refcount rc;                   // delete yourself if no one cares about you
 
-       bool auth;
-       char *name;                 // player name
-       pthread_rwlock_t auth_lock; // why
+       DragonnetPeer *peer;           // not to be confused with beer
+       pthread_rwlock_t lock_peer;    // programming socks make you 100% cuter
 
-       v3f64 pos;                  // player position
-       pthread_rwlock_t pos_lock;  // i want to commit die
+       bool auth;                     // YES OR NO I DEMAND AN ANSWER
+       char *name;                    // player name
+       pthread_rwlock_t lock_auth;    // poggers based cringe
+
+       v3f64 pos;                     // player position
+       v3f32 rot;                     // you wont guess what this is
+       pthread_rwlock_t lock_pos;     // git commit crime
 } ServerPlayer;
 
 void server_player_init();
@@ -26,14 +29,11 @@ void server_player_deinit();
 void server_player_add(DragonnetPeer *peer);
 void server_player_remove(DragonnetPeer *peer);
 
-u64 server_player_find(char *name);
-
 ServerPlayer *server_player_grab(u64 id);
-void server_player_drop(ServerPlayer *player);
+ServerPlayer *server_player_grab_named(char *name);
 
 bool server_player_auth(ServerPlayer *player, char *name);
 void server_player_disconnect(ServerPlayer *player);
-void server_player_send_pos(ServerPlayer *player);
-void server_player_iterate(void (cb)(ServerPlayer *, void *), void *arg);
+void server_player_iterate(void *func, void *arg);
 
-#endif
+#endif // _SERVER_PLAYER_H_
diff --git a/src/server/server_terrain.c b/src/server/server_terrain.c
new file mode 100644 (file)
index 0000000..a4aa91e
--- /dev/null
@@ -0,0 +1,428 @@
+#define _GNU_SOURCE // don't worry, GNU extensions are only used when available
+#include <dragonstd/queue.h>
+#include <features.h>
+#include <stdatomic.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "interrupt.h"
+#include "server/database.h"
+#include "server/schematic.h"
+#include "server/server_config.h"
+#include "server/server_terrain.h"
+#include "server/terrain_gen.h"
+#include "terrain.h"
+
+// this file is too long
+Terrain *server_terrain;
+
+static atomic_bool cancel;                 // remove the smooth
+static Queue terrain_gen_tasks;            // this is terry the fat shark
+static pthread_t *terrain_gen_threads;     // thread pool
+static s32 spawn_height;                   // elevation to spawn players at
+static unsigned int num_gen_chunks;        // number of enqueued / generating chunks
+static pthread_mutex_t mtx_num_gen_chunks; // lock to protect the above
+
+// utility functions
+
+// return true if a player is close enough to a chunk to access it
+static bool within_load_distance(ServerPlayer *player, v3s32 cpos, u32 dist)
+{
+       pthread_rwlock_rdlock(&player->lock_pos);
+       v3s32 ppos = terrain_node_to_chunk_pos((v3s32) {player->pos.x, player->pos.y, player->pos.z}, NULL);
+       pthread_rwlock_unlock(&player->lock_pos);
+
+       return abs(ppos.x - cpos.x) <= (s32) dist
+               && abs(ppos.y - cpos.y) <= (s32) dist
+               && abs(ppos.z - cpos.z) <= (s32) dist;
+}
+
+// send a chunk to a client and reset chunk request
+static void send_chunk(ServerPlayer *player, TerrainChunk *chunk)
+{
+       if (!within_load_distance(player, chunk->pos, server_config.load_distance))
+               return;
+
+       pthread_rwlock_rdlock(&player->lock_peer);
+       if (player->peer)
+               dragonnet_peer_send_ToClientChunk(player->peer, &(ToClientChunk) {
+                       .pos = chunk->pos,
+                       .data = ((TerrainChunkMeta *) chunk->extra)->data,
+               });
+       pthread_rwlock_unlock(&player->lock_peer);
+}
+
+// send chunk to near clients
+// chunk mutex has to be locked
+static void send_chunk_to_near(TerrainChunk *chunk)
+{
+       TerrainChunkMeta *meta = chunk->extra;
+
+       if (meta->state == CHUNK_GENERATING)
+               return;
+
+       Blob_free(&meta->data);
+       meta->data = terrain_serialize_chunk(chunk);
+
+       database_save_chunk(chunk);
+
+       if (meta->state == CHUNK_CREATED)
+               return;
+
+       server_player_iterate((void *) &send_chunk, chunk);
+}
+
+// Iterator for sending changed chunks to near clients
+static void iterator_send_chunk_to_near(TerrainChunk *chunk)
+{
+       pthread_mutex_lock(&chunk->mtx);
+       send_chunk_to_near(chunk);
+       pthread_mutex_unlock(&chunk->mtx);
+}
+
+// me when the
+static void terrain_gen_step()
+{
+       // big chunkus
+       TerrainChunk *chunk = queue_deq(&terrain_gen_tasks, NULL);
+
+       if (!chunk)
+               return;
+
+       TerrainChunkMeta *meta = chunk->extra;
+
+       List changed_chunks;
+       list_ini(&changed_chunks);
+       list_apd(&changed_chunks, chunk);
+
+       terrain_gen_chunk(chunk, &changed_chunks);
+
+       pthread_mutex_lock(&chunk->mtx);
+       meta->state = CHUNK_READY;
+       pthread_mutex_unlock(&chunk->mtx);
+
+       list_clr(&changed_chunks, (void *) &iterator_send_chunk_to_near, NULL, NULL);
+
+       pthread_mutex_lock(&mtx_num_gen_chunks);
+       num_gen_chunks--;
+       pthread_mutex_unlock(&mtx_num_gen_chunks);
+}
+
+// there was a time when i wrote actually useful comments lol
+static void *terrain_gen_thread()
+{
+#ifdef __GLIBC__ // check whether bloat is enabled
+       pthread_setname_np(pthread_self(), "terrain_gen");
+#endif // __GLIBC__
+
+       // extremely advanced logic
+       while (!cancel)
+               terrain_gen_step();
+
+       return NULL;
+}
+
+// enqueue chunk
+static void generate_chunk(TerrainChunk *chunk)
+{
+       if (cancel)
+               return;
+
+       pthread_mutex_lock(&mtx_num_gen_chunks);
+       num_gen_chunks++;
+       pthread_mutex_unlock(&mtx_num_gen_chunks);
+
+       TerrainChunkMeta *meta = chunk->extra;
+       meta->state = CHUNK_GENERATING;
+       queue_enq(&terrain_gen_tasks, chunk);
+}
+
+// terrain callbacks
+// note: all these functions require the chunk mutex to be locked, which is always the case when a terrain callback is invoked
+
+// callback for initializing a newly created chunk
+// load chunk from database or initialize state, tgstage buffer and data
+static void on_create_chunk(TerrainChunk *chunk)
+{
+       TerrainChunkMeta *meta = chunk->extra = malloc(sizeof *meta);
+
+       if (!database_load_chunk(chunk)) {
+               meta->state = CHUNK_CREATED;
+               meta->data = (Blob) {0, NULL};
+
+               CHUNK_ITERATE {
+                       chunk->data[x][y][z] = terrain_node_create(NODE_AIR, (Blob) {0, NULL});
+                       meta->tgsb.raw.nodes[x][y][z] = STAGE_VOID;
+               }
+       }
+}
+
+// callback for deleting a chunk
+// free meta data
+static void on_delete_chunk(TerrainChunk *chunk)
+{
+       TerrainChunkMeta *meta = chunk->extra;
+
+       Blob_free(&meta->data);
+       free(meta);
+}
+
+// callback for determining whether a chunk should be returned by terrain_get_chunk
+// hold back chunks that are not fully generated except when the create flag is set to true
+static bool on_get_chunk(TerrainChunk *chunk, bool create)
+{
+       TerrainChunkMeta *meta = chunk->extra;
+
+       if (meta->state < CHUNK_READY && !create)
+               return false;
+
+       return true;
+}
+
+// callback for deciding whether a set_node call succeeds or not
+// reject set_node calls that try to override nodes placed by later terraingen stages, else update tgs buffer - also make sure chunk is inserted into changed_chunks list
+static bool on_set_node(TerrainChunk *chunk, v3u8 offset, __attribute__((unused)) TerrainNode *node, void *_arg)
+{
+       TerrainSetNodeArg *arg = _arg;
+
+       TerrainGenStage new_tgs = arg ? arg->tgs : STAGE_PLAYER;
+       TerrainGenStage *tgs = &((TerrainChunkMeta *) chunk->extra)->
+               tgsb.raw.nodes[offset.x][offset.y][offset.z];
+
+       if (new_tgs >= *tgs) {
+               *tgs = new_tgs;
+
+               if (arg)
+                       list_add(arg->changed_chunks, chunk, chunk, &cmp_ref, NULL);
+
+               return true;
+       }
+
+       return false;
+}
+
+// callback for when chunk content changes
+// send chunk to near clients if not part of terrain generation
+static void on_after_set_node(TerrainChunk *chunk, __attribute__((unused)) v3u8 offset, void *arg)
+{
+       if (!arg)
+               send_chunk_to_near(chunk);
+}
+
+// generate a hut for new players to spawn in
+static void generate_spawn_hut()
+{
+       List changed_chunks;
+       list_ini(&changed_chunks);
+
+       List spawn_hut;
+       schematic_load(&spawn_hut, RESSOURCE_PATH "schematics/spawn_hut.txt", (SchematicMapping[]) {
+               {
+                       .color = {0x7d, 0x54, 0x35},
+                       .type = NODE_OAK_WOOD,
+                       .use_color = true,
+               },
+               {
+                       .color = {0x50, 0x37, 0x28},
+                       .type = NODE_OAK_WOOD,
+                       .use_color = true,
+               },
+       }, 2);
+
+       schematic_place(&spawn_hut, (v3s32) {0, spawn_height, 0},
+               STAGE_PLAYER, &changed_chunks);
+
+       schematic_delete(&spawn_hut);
+
+       // dynamic part of spawn hut - cannot be generated by a schematic
+
+       v2s32 posts[6] = {
+               {-4, -2},
+               {-4, +3},
+               {+3, -2},
+               {+3, +3},
+               {+4, +0},
+               {+4, +1},
+       };
+
+       Blob wood_color = {0, NULL};
+       ColorData_write(&wood_color, &(ColorData) {{(f32) 0x7d / 0xff, (f32) 0x54 / 0xff, (f32) 0x35 / 0xff}});
+
+       for (int i = 0; i < 6; i++) {
+               for (s32 y = spawn_height - 1;; y--) {
+                       v3s32 pos = {posts[i].x, y, posts[i].y};
+                       NodeType node = terrain_get_node(server_terrain, pos).type;
+
+                       if (i >= 4) {
+                               if (node != NODE_AIR)
+                                       break;
+
+                               pos.y++;
+                       }
+
+                       if (node_definitions[node].solid)
+                               break;
+
+                       server_terrain_gen_node(pos,
+                               terrain_node_create(node == NODE_LAVA
+                                               ? NODE_VULCANO_STONE
+                                               : NODE_OAK_WOOD,
+                                       wood_color),
+                               STAGE_PLAYER, &changed_chunks);
+               }
+       }
+
+       Blob_free(&wood_color);
+       list_clr(&changed_chunks, (void *) iterator_send_chunk_to_near, NULL, NULL);
+}
+
+// public functions
+
+// called on server startup
+void server_terrain_init()
+{
+       server_terrain = terrain_create();
+       server_terrain->callbacks.create_chunk   = &on_create_chunk;
+       server_terrain->callbacks.delete_chunk   = &on_delete_chunk;
+       server_terrain->callbacks.get_chunk      = &on_get_chunk;
+       server_terrain->callbacks.set_node       = &on_set_node;
+       server_terrain->callbacks.after_set_node = &on_after_set_node;
+
+       cancel = false;
+       queue_ini(&terrain_gen_tasks);
+       terrain_gen_threads = malloc(sizeof *terrain_gen_threads * server_config.terrain_gen_threads);
+       num_gen_chunks = 0;
+       pthread_mutex_init(&mtx_num_gen_chunks, NULL);
+
+       for (unsigned int i = 0; i < server_config.terrain_gen_threads; i++)
+               pthread_create(&terrain_gen_threads[i], NULL, (void *) &terrain_gen_thread, NULL);
+}
+
+// called on server shutdown
+void server_terrain_deinit()
+{
+       queue_fin(&terrain_gen_tasks);
+       cancel = true;
+       queue_cnl(&terrain_gen_tasks);
+
+       for (unsigned int i = 0; i < server_config.terrain_gen_threads; i++)
+               pthread_join(terrain_gen_threads[i], NULL);
+       free(terrain_gen_threads);
+
+       pthread_mutex_destroy(&mtx_num_gen_chunks);
+       queue_dst(&terrain_gen_tasks);
+       terrain_delete(server_terrain);
+}
+
+// handle chunk request from client (thread safe)
+void server_terrain_requested_chunk(ServerPlayer *player, v3s32 pos)
+{
+       if (within_load_distance(player, pos, server_config.load_distance)) {
+               TerrainChunk *chunk = terrain_get_chunk(server_terrain, pos, true);
+
+               pthread_mutex_lock(&chunk->mtx);
+
+               TerrainChunkMeta *meta = chunk->extra;
+               switch (meta->state) {
+                       case CHUNK_CREATED:
+                               generate_chunk(chunk);
+                               break;
+
+                       case CHUNK_GENERATING:
+                               break;
+
+                       case CHUNK_READY:
+                               send_chunk(player, chunk);
+               };
+
+               pthread_mutex_unlock(&chunk->mtx);
+       }
+}
+
+static void update_percentage()
+{
+       static s32 total = 3 * 3 * 21;
+       static s32 done = -1;
+       static s32 last_percentage = -1;
+
+       if (done < total)
+               done++;
+
+       pthread_mutex_lock(&mtx_num_gen_chunks);
+       s32 percentage = 100.0 * (done - num_gen_chunks) / total;
+       pthread_mutex_unlock(&mtx_num_gen_chunks);
+
+       if (percentage > last_percentage) {
+               last_percentage = percentage;
+               printf("[verbose] preparing spawn... %d%%\n", percentage);
+       }
+
+}
+
+// prepare spawn region
+void server_terrain_prepare_spawn()
+{
+       update_percentage();
+
+       for (s32 x = -1; x <= (s32) 1; x++) {
+               for (s32 y = -10; y <= (s32) 10; y++) {
+                       for (s32 z = -1; z <= (s32) 1; z++) {
+                               if (interrupt.set)
+                                       return;
+
+                               TerrainChunk *chunk = terrain_get_chunk(server_terrain, (v3s32) {x, y, z}, true);
+
+                               pthread_mutex_lock(&chunk->mtx);
+                               if (((TerrainChunkMeta *) chunk->extra)->state == CHUNK_CREATED)
+                                       generate_chunk(chunk);
+                               pthread_mutex_unlock(&chunk->mtx);
+
+                               update_percentage();
+                       }
+               }
+       }
+
+       for (;;) {
+               update_percentage();
+
+               pthread_mutex_lock(&mtx_num_gen_chunks);
+               bool done = (num_gen_chunks == 0);
+               pthread_mutex_unlock(&mtx_num_gen_chunks);
+
+               if (done)
+                       break;
+
+               sched_yield();
+       }
+
+       s64 saved_spawn_height;
+       if (database_load_meta("spawn_height", &saved_spawn_height)) {
+               spawn_height = saved_spawn_height;
+       } else {
+               spawn_height = -1;
+               while (terrain_get_node(server_terrain, (v3s32) {0, ++spawn_height, 0}).type != NODE_AIR);
+               spawn_height += 5;
+
+               database_save_meta("spawn_height", spawn_height);
+               generate_spawn_hut();
+       }
+}
+
+void server_terrain_gen_node(v3s32 pos, TerrainNode node, TerrainGenStage tgs, List *changed_chunks)
+{
+       TerrainSetNodeArg arg = {
+               .tgs = tgs,
+               .changed_chunks = changed_chunks,
+       };
+
+       terrain_set_node(server_terrain, pos, node, true, &arg);
+}
+
+s32 server_terrain_spawn_height()
+{
+       // wow, so useful!
+       return spawn_height;
+}
diff --git a/src/server/server_terrain.h b/src/server/server_terrain.h
new file mode 100644 (file)
index 0000000..88fa7a8
--- /dev/null
@@ -0,0 +1,44 @@
+#ifndef _SERVER_TERRAIN_H_
+#define _SERVER_TERRAIN_H_
+
+#include <pthread.h>
+#include "server/server_player.h"
+#include "terrain.h"
+#include "types.h"
+
+typedef enum {
+       CHUNK_CREATED,    // chunk exists but was not yet generated
+       CHUNK_GENERATING, // currently generating in a seperate thread
+       CHUNK_READY,      // generation finished
+} TerrainChunkState;
+
+typedef enum {
+       STAGE_VOID,     // initial air, can be overridden by anything
+       STAGE_TERRAIN,  // basic terrain, can be overridden by anything except the void
+       STAGE_BOULDERS, // boulders, replace terrain
+       STAGE_TREES,    // trees replace boulders
+       STAGE_PLAYER,   // player-placed nodes or things placed after terrain generation
+} TerrainGenStage;
+
+typedef struct {
+       TerrainGenStage tgs;
+       List *changed_chunks;
+} TerrainSetNodeArg;
+
+typedef struct {
+       Blob data;                  // the big cum
+       TerrainChunkState state;    // generation state of the chunk
+       pthread_t gen_thread;       // thread that is generating chunk
+       TerrainGenStageBuffer tgsb; // buffer to make sure terraingen only overrides things it should
+} TerrainChunkMeta; // OMG META VERSE WEB 3.0 VIRTUAL REALITY
+
+extern Terrain *server_terrain; // terrain object, data is stored here
+
+void server_terrain_init();                                                                           // called on server startup
+void server_terrain_deinit();                                                                         // called on server shutdown
+void server_terrain_requested_chunk(ServerPlayer *player, v3s32 pos);                                 // handle chunk request from client (thread safe)
+void server_terrain_prepare_spawn();                                                                  // prepare spawn region
+void server_terrain_gen_node(v3s32 pos, TerrainNode node, TerrainGenStage tgs, List *changed_chunks); // set node with terraingen stage
+s32 server_terrain_spawn_height();                                                                    // get the spawn height because idk
+
+#endif // _SERVER_TERRAIN_H_
diff --git a/src/server/terrain_gen.c b/src/server/terrain_gen.c
new file mode 100644 (file)
index 0000000..5b0c1d1
--- /dev/null
@@ -0,0 +1,113 @@
+#include <math.h>
+#include <stdlib.h>
+#include "environment.h"
+#include "perlin.h"
+#include "server/biomes.h"
+#include "server/server_terrain.h"
+#include "server/terrain_gen.h"
+#include "server/trees.h"
+
+// generate a chunk (does not manage chunk state or threading)
+void terrain_gen_chunk(TerrainChunk *chunk, List *changed_chunks)
+{
+       TerrainChunkMeta *meta = chunk->extra;
+
+       BiomeArgsChunk chunk_args;
+       BiomeArgsRow row_args;
+       BiomeArgsHeight height_args;
+       BiomeArgsGenerate generate_args;
+       TreeArgsCondition condition_args;
+
+       chunk_args.chunk = condition_args.chunk = chunk;
+       chunk_args.changed_chunks = generate_args.changed_chunks = changed_chunks;
+
+       v3s32 chunkp = {
+               chunk->pos.x * CHUNK_SIZE,
+               chunk->pos.y * CHUNK_SIZE,
+               chunk->pos.z * CHUNK_SIZE,
+       };
+
+       unsigned char *chunk_data[COUNT_BIOME] = {NULL};
+       bool chunk_called[COUNT_BIOME] = {false};
+
+       for (u8 x = 0; x < CHUNK_SIZE; x++) {
+               s32 pos_x = chunkp.x + x;
+
+               for (u8 z = 0; z < CHUNK_SIZE; z++) {
+                       row_args.pos = height_args.pos = (v2s32) {pos_x, chunkp.z + z};
+
+                       condition_args.biome = get_biome(row_args.pos, &condition_args.factor);
+                       BiomeDef *biome_def = &biomes[condition_args.biome];
+
+                       height_args.factor = generate_args.factor = row_args.factor
+                               = condition_args.factor;
+
+                       if (biome_def->chunk_data_size && !chunk_data[condition_args.biome])
+                               chunk_data[condition_args.biome] =      malloc(biome_def->chunk_data_size);
+
+                       chunk_args.chunk_data = row_args.chunk_data = height_args.chunk_data =
+                               generate_args.chunk_data = condition_args.chunk_data =
+                               chunk_data[condition_args.biome];
+
+                       if (biome_def->chunk && !chunk_called[condition_args.biome]) {
+                               biome_def->chunk(&chunk_args);
+                               chunk_called[condition_args.biome] = true;
+                       }
+
+                       unsigned char row_data[biome_def->row_data_size];
+                       row_args.row_data = height_args.row_data = generate_args.row_data =
+                               condition_args.row_data = row_data;
+
+                       if (biome_def->row)
+                               biome_def->row(&row_args);
+
+                       height_args.height = 1.0
+                               * (pnoise2d(U32(height_args.pos.x) /  32.0, U32(height_args.pos.y) /  32.0, 0.45, 5, seed + SO_HEIGHT)    * 16.0 + 0.0)
+                               * (pnoise2d(U32(height_args.pos.x) / 256.0, U32(height_args.pos.y) / 256.0, 0.45, 5, seed + SO_HILLYNESS) *  0.5 + 0.5)
+                               + 32.0;
+
+                       s32 height = biome_def->height(&height_args);
+
+                       for (u8 y = 0; y < CHUNK_SIZE; y++) {
+                               generate_args.pos = condition_args.pos = (v3s32)
+                                       {row_args.pos.x, chunkp.y + y, row_args.pos.y};
+                               generate_args.diff = generate_args.pos.y - height;
+
+                               generate_args.humidity = condition_args.humidity =
+                                       get_humidity(generate_args.pos);
+                               generate_args.temperature = condition_args.temperature =
+                                       get_temperature(generate_args.pos);
+
+                               NodeType node = biome_def->generate(&generate_args);
+
+                               if (biome_def->snow
+                                               && generate_args.diff <= 1
+                                               && generate_args.temperature < 0.0
+                                               && node == NODE_AIR)
+                                       node = NODE_SNOW;
+
+                               if (generate_args.diff == 1) for (int i = 0; i < NUM_TREES; i++) {
+                                       TreeDef *def = &tree_definitions[i];
+
+                                       if (def->condition(&condition_args)
+                                                       && noise2d(condition_args.pos.x, condition_args.pos.z, 0, seed + def->offset) * 0.5 + 0.5 < def->probability
+                                                       && smooth2d(U32(condition_args.pos.x) / def->spread, U32(condition_args.pos.z) / def->spread, 0, seed + def->area_offset) * 0.5 + 0.5 < def->area_probability) {
+                                               def->generate(condition_args.pos, changed_chunks);
+                                               break;
+                                       }
+                               }
+
+                               pthread_mutex_lock(&chunk->mtx);
+                               if (meta->tgsb.raw.nodes[x][y][z] <= STAGE_TERRAIN) {
+                                       chunk->data[x][y][z] = terrain_node_create(node, (Blob) {0, NULL});
+                                       meta->tgsb.raw.nodes[x][y][z] = STAGE_TERRAIN;
+                               }
+                               pthread_mutex_unlock(&chunk->mtx);
+                       }
+               }
+       }
+
+       for (Biome i = 0; i < COUNT_BIOME; i++)
+               if (chunk_data[i])
+                       free(chunk_data[i]);
+}
diff --git a/src/server/terrain_gen.h b/src/server/terrain_gen.h
new file mode 100644 (file)
index 0000000..76bf813
--- /dev/null
@@ -0,0 +1,9 @@
+#ifndef _TERRAIN_GEN_H_
+#define _TERRAIN_GEN_H_
+
+#include "server/server_terrain.h"
+#include "terrain.h"
+
+void terrain_gen_chunk(TerrainChunk *chunk, List *changed_chunks); // generate a chunk (does not manage chunk state or threading)
+
+#endif // _TERRAIN_GEN_H_
index 31dc6c76e82bd41eaeb76b0705ee877f79673e32..4de22852259e2b7d4aea2cf9c27cc9e546f772be 100644 (file)
@@ -1,21 +1,19 @@
-#include <stdio.h>
 #include <stdlib.h>
 #include "server/biomes.h"
-#include "server/mapgen.h"
+#include "server/server_terrain.h"
 #include "server/trees.h"
 #include "server/voxelctx.h"
-#include "util.h"
 
 // oak
 
-static bool oak_condition(unused v3s32 pos, unused f64 humidity, unused f64 temperature, Biome biome, unused f64 factor, unused MapBlock *block, unused void *row_data, unused void *block_data)
+static bool oak_condition(TreeArgsCondition *args)
 {
-       return biome == BIOME_HILLS;
+       return args->biome == BIOME_HILLS;
 }
 
 static void oak_tree_leaf(Voxelctx *ctx)
 {
-       if (! voxelctx_is_alive(ctx))
+       if (!voxelctx_is_alive(ctx))
                return;
 
        voxelctx_push(ctx);
@@ -35,7 +33,7 @@ static void oak_tree_leaf(Voxelctx *ctx)
 
 static void oak_tree_top(Voxelctx *ctx)
 {
-       if (! voxelctx_is_alive(ctx))
+       if (!voxelctx_is_alive(ctx))
                return;
 
        voxelctx_push(ctx);
@@ -58,7 +56,7 @@ static void oak_tree_top(Voxelctx *ctx)
 
 static void oak_tree_part(Voxelctx *ctx, f32 n)
 {
-       if (! voxelctx_is_alive(ctx))
+       if (!voxelctx_is_alive(ctx))
                return;
 
        voxelctx_push(ctx);
@@ -83,9 +81,9 @@ static void oak_tree_part(Voxelctx *ctx, f32 n)
        voxelctx_pop(ctx);
 }
 
-static void oak_tree(v3s32 pos, List *changed_blocks)
+static void oak_tree(v3s32 pos, List *changed_chunks)
 {
-       Voxelctx *ctx = voxelctx_create(changed_blocks, MGS_TREES, pos);
+       Voxelctx *ctx = voxelctx_create(changed_chunks, STAGE_TREES, pos);
 
        voxelctx_hue(ctx, 40.0f);
        voxelctx_light(ctx, -0.5f);
@@ -109,12 +107,12 @@ static void oak_tree(v3s32 pos, List *changed_blocks)
 
 // pine
 
-static bool pine_condition(unused v3s32 pos, unused f64 humidity, unused f64 temperature, Biome biome, unused f64 factor, unused MapBlock *block, unused void *row_data, unused void *block_data)
+static bool pine_condition(TreeArgsCondition *args)
 {
-       return biome == BIOME_MOUNTAIN;
+       return args->biome == BIOME_MOUNTAIN;
 }
 
-static void pine_tree(v3s32 pos, List *changed_blocks)
+static void pine_tree(v3s32 pos, List *changed_chunks)
 {
        s32 tree_top = (noise2d(pos.x, pos.z, 0, seed + SO_PINETREE_HEIGHT) * 0.5 + 0.5) * (35.0 - 20.0) + 20.0 + pos.y;
        for (v3s32 tree_pos = pos; tree_pos.y < tree_top; tree_pos.y++) {
@@ -130,24 +128,28 @@ static void pine_tree(v3s32 pos, List *changed_blocks)
                s32 dir = (noise3d(tree_pos.x, tree_pos.y, tree_pos.z, 0, seed + SO_PINETREE_BRANCH_DIR) * 0.5 + 0.5) * 4.0;
 
                for (v3s32 branch_pos = tree_pos; branch_length > 0; branch_length--, branch_pos = v3s32_add(branch_pos, dirs[dir]))
-                       mapgen_set_node(branch_pos, map_node_create(NODE_PINE_WOOD, (Blob) {0, NULL}), MGS_TREES, changed_blocks);
+                       server_terrain_gen_node(branch_pos,
+                               terrain_node_create(NODE_PINE_WOOD, (Blob) {0, NULL}),
+                               STAGE_TREES, changed_chunks);
 
-               mapgen_set_node(tree_pos, map_node_create(NODE_PINE_WOOD, (Blob) {0, NULL}), MGS_TREES, changed_blocks);
+               server_terrain_gen_node(tree_pos,
+                       terrain_node_create(NODE_PINE_WOOD, (Blob) {0, NULL}),
+                       STAGE_TREES, changed_chunks);
        }
 }
 
 // palm
 
-static bool palm_condition(v3s32 pos, unused f64 humidity, unused f64 temperature, Biome biome, unused f64 factor, unused MapBlock *block, void *row_data, unused void *block_data)
+static bool palm_condition(TreeArgsCondition *args)
 {
-       return biome == BIOME_OCEAN
-               && ocean_get_node_at((v3s32) {pos.x, pos.y - 0, pos.z}, 1, row_data) == NODE_AIR
-               && ocean_get_node_at((v3s32) {pos.x, pos.y - 1, pos.z}, 0, row_data) == NODE_SAND;
+       return args->biome == BIOME_OCEAN
+               && ocean_get_node_at((v3s32) {args->pos.x, args->pos.y - 0, args->pos.z}, 1, args->row_data) == NODE_AIR
+               && ocean_get_node_at((v3s32) {args->pos.x, args->pos.y - 1, args->pos.z}, 0, args->row_data) == NODE_SAND;
 }
 
 static void palm_branch(Voxelctx *ctx)
 {
-       if (! voxelctx_is_alive(ctx))
+       if (!voxelctx_is_alive(ctx))
                return;
 
        voxelctx_cube(ctx, NODE_PALM_LEAVES, true);
@@ -160,9 +162,9 @@ static void palm_branch(Voxelctx *ctx)
        voxelctx_pop(ctx);
 }
 
-static void palm_tree(v3s32 pos, List *changed_blocks)
+static void palm_tree(v3s32 pos, List *changed_chunks)
 {
-       Voxelctx *ctx = voxelctx_create(changed_blocks, MGS_TREES, (v3s32) {pos.x, pos.y - 1, pos.z});
+       Voxelctx *ctx = voxelctx_create(changed_chunks, STAGE_TREES, (v3s32) {pos.x, pos.y - 1, pos.z});
 
        f32 s = voxelctx_random(ctx, 8.0f, 2.0f);
 
index 3bc6763af75572f3146a325b041da0d2feeb53f1..8084c52d2f242f16b0c16382dfd64aae36ca128b 100644 (file)
@@ -2,22 +2,34 @@
 #define _TREES_H_
 
 #include <dragonstd/list.h>
+#include <stdbool.h>
 #include "perlin.h"
+#include "terrain.h"
 #include "types.h"
 
 #define NUM_TREES 3
 
-typedef struct
-{
+typedef struct {
+       v3s32 pos;
+       f64 humidity;
+       f64 temperature;
+       Biome biome;
+       f64 factor;
+       TerrainChunk *chunk;
+       void *row_data;
+       void *chunk_data;
+} TreeArgsCondition;
+
+typedef struct {
        f32 spread;
        f32 probability;
        f32 area_probability;
        SeedOffset offset;
        SeedOffset area_offset;
-       bool (*condition)(v3s32 pos, f64 humidity, f64 temperature, Biome biome, f64 factor, MapBlock *block, void *row_data, void *block_data);
-       void (*generate)(v3s32 pos, List *changed_blocks);
+       bool (*condition)(TreeArgsCondition *args);
+       void (*generate)(v3s32 pos, List *changed_chunks);
 } TreeDef;
 
 extern TreeDef tree_definitions[];
 
-#endif
+#endif // _TREES_H_
index 6f5ee208b240d31acae08214e562eb5e708e77cb..bea87fd76e932ace14fda44e4ac6daeb317e617b 100644 (file)
@@ -1,13 +1,13 @@
 #include <stdlib.h>
 #include <pthread.h>
-#include "server/mapgen.h"
-#include "server/voxelctx.h"
+#include "color.h"
 #include "perlin.h"
-#include "util.h"
+#include "server/terrain_gen.h"
+#include "server/voxelctx.h"
 
 static VoxelctxState *create_state(VoxelctxState *old)
 {
-       VoxelctxState *state = malloc(sizeof(VoxelctxState));
+       VoxelctxState *state = malloc(sizeof *state);
 
        if (old) {
                *state = *old;
@@ -29,29 +29,24 @@ static VoxelctxState *create_state(VoxelctxState *old)
        return state;
 }
 
-Voxelctx *voxelctx_create(List *changed_blocks, MapgenStage mgs, v3s32 pos)
+Voxelctx *voxelctx_create(List *changed_chunks, TerrainGenStage tgs, v3s32 pos)
 {
        Voxelctx *ctx = malloc(sizeof(Voxelctx));
 
-       ctx->changed_blocks = changed_blocks;
-       ctx->mgs = mgs;
+       ctx->changed_chunks = changed_chunks;
+       ctx->tgs = tgs;
        ctx->pos = pos;
-       ctx->statestack = list_create(NULL);
+       list_ini(&ctx->statestack);
        ctx->random = 0;
 
-       list_put(&ctx->statestack, create_state(NULL), NULL);
+       list_apd(&ctx->statestack, create_state(NULL));
 
        return ctx;
 }
 
-static void list_delete_state(void *key, unused void *value, unused void *arg)
-{
-       free(key);
-}
-
 void voxelctx_delete(Voxelctx *ctx)
 {
-       list_clear_func(&ctx->statestack, &list_delete_state, NULL);
+       list_clr(&ctx->statestack, (void *) &free, NULL, NULL);
        free(ctx);
 }
 
@@ -150,20 +145,13 @@ void voxelctx_s(Voxelctx *ctx, f32 value)
 
 void voxelctx_pop(Voxelctx *ctx)
 {
-       ListPair *next = ctx->statestack.first->next;
-       free(ctx->statestack.first->key);
-       free(ctx->statestack.first);
-       ctx->statestack.first = next;
+       free(ctx->statestack.fst->dat);
+       list_nrm(&ctx->statestack, &ctx->statestack.fst);
 }
 
 void voxelctx_push(Voxelctx *ctx)
 {
-       ListPair *pair = malloc(sizeof(ListPair));
-       pair->key = create_state(&VOXELCTXSTATE(ctx));
-       pair->value = NULL;
-       pair->next = ctx->statestack.first;
-
-       ctx->statestack.first = pair;
+       list_ppd(&ctx->statestack, create_state(&VOXELCTXSTATE(ctx)));
 }
 
 bool voxelctx_is_alive(Voxelctx *ctx)
@@ -177,9 +165,9 @@ bool voxelctx_is_alive(Voxelctx *ctx)
        return VOXELCTXSTATE(ctx).scale[0] >= 1.0f && VOXELCTXSTATE(ctx).scale[1] >= 1.0f && VOXELCTXSTATE(ctx).scale[2] >= 1.0f;
 }
 
-void voxelctx_cube(Voxelctx *ctx, Node node, bool use_color)
+void voxelctx_cube(Voxelctx *ctx, NodeType node, bool use_color)
 {
-       if (! voxelctx_is_alive(ctx))
+       if (!voxelctx_is_alive(ctx))
                return;
 
        vec4 base_corners[8] = {
@@ -230,23 +218,25 @@ void voxelctx_cube(Voxelctx *ctx, Node node, bool use_color)
                Blob buffer = {0, NULL};
 
                if (use_color)
-                       HSLData_write(&buffer, &(HSLData) {{
+                       ColorData_write(&buffer, &(ColorData) {hsl_to_rgb((v3f32) {
                                VOXELCTXSTATE(ctx).h / 360.0,
                                VOXELCTXSTATE(ctx).s,
                                VOXELCTXSTATE(ctx).l,
-                       }});
+                       })});
 
-               mapgen_set_node(
+               server_terrain_gen_node(
                        v3s32_add(ctx->pos, (v3s32) {v[0], v[2], v[1]}),
-                       map_node_create(node, buffer),
-                       ctx->mgs,
-                       ctx->changed_blocks
+                       terrain_node_create(node, buffer),
+                       ctx->tgs,
+                       ctx->changed_chunks
                );
+
+               Blob_free(&buffer);
        }
 }
 
 
-void voxelctx_cylinder(Voxelctx *ctx, Node node, bool use_color)
+void voxelctx_cylinder(Voxelctx *ctx, NodeType node, bool use_color)
 {
        voxelctx_cube(ctx, node, use_color);
 }
@@ -254,7 +244,7 @@ void voxelctx_cylinder(Voxelctx *ctx, Node node, bool use_color)
 /*
 void voxelctx_cylinder(Voxelctx *ctx, Node node, bool use_color)
 {
-       if (! voxelctx_is_alive(ctx))
+       if (!voxelctx_is_alive(ctx))
                return;
 
        return;
@@ -269,7 +259,7 @@ void voxelctx_cylinder(Voxelctx *ctx, Node node, bool use_color)
                                        ctx->pos.x + round(VOXELCTXSTATE(ctx).pos[0] + x),
                                        ctx->pos.y + round(VOXELCTXSTATE(ctx).pos[2] + z),
                                        ctx->pos.z + round(VOXELCTXSTATE(ctx).pos[1] + y),
-                               }, CREATE_NODE, ctx->mgs, ctx->changed_blocks);
+                               }, CREATE_NODE, ctx->tgs, ctx->changed_chunks);
                        }
                }
        }
index 42618b357d4738c11f427496560607f4a3c22fd3..2469be0e4dec69c283956f8264547b5b629c82e6 100644 (file)
@@ -1,15 +1,14 @@
 #ifndef _VOXELCTX_H_
 #define _VOXELCTX_H_
 
-#define VOXELCTXSTATE(ctx) (*((VoxelctxState *) (ctx)->statestack.first->key))
+#define VOXELCTXSTATE(ctx) (*((VoxelctxState *) (ctx)->statestack.fst->dat))
 
-#include <linmath.h/linmath.h>
 #include <dragonstd/list.h>
+#include <linmath.h/linmath.h>
+#include "server/server_terrain.h"
 #include "types.h"
-#include "server/server_map.h"
 
-typedef struct
-{
+typedef struct {
        vec4 pos;
        vec3 scale;
        mat4x4 mat;
@@ -17,16 +16,15 @@ typedef struct
        s32 life;
 } VoxelctxState;
 
-typedef struct
-{
+typedef struct {
        v3s32 pos;
-       List *changed_blocks;
-       MapgenStage mgs;
+       List *changed_chunks;
+       TerrainGenStage tgs;
        List statestack;
        s32 random;
 } Voxelctx;
 
-Voxelctx *voxelctx_create(List *changed_blocks, MapgenStage mgs, v3s32 pos);
+Voxelctx *voxelctx_create(List *changed_chunks, TerrainGenStage tgs, v3s32 pos);
 void voxelctx_delete(Voxelctx *ctx);
 void voxelctx_hue(Voxelctx *ctx, f32 value);
 void voxelctx_sat(Voxelctx *ctx, f32 value);
@@ -45,8 +43,8 @@ void voxelctx_s(Voxelctx *ctx, f32 value);
 void voxelctx_pop(Voxelctx *ctx);
 void voxelctx_push(Voxelctx *ctx);
 bool voxelctx_is_alive(Voxelctx *ctx);
-void voxelctx_cube(Voxelctx *ctx, Node node, bool use_hsl);
-void voxelctx_cylinder(Voxelctx *ctx, Node node, bool use_hsl);
+void voxelctx_cube(Voxelctx *ctx, NodeType node, bool use_color);
+void voxelctx_cylinder(Voxelctx *ctx, NodeType node, bool use_color);
 f32 voxelctx_random(Voxelctx *ctx, f32 base, f32 vary);
 
-#endif
+#endif // _VOXELCTX_H_
diff --git a/src/terrain.c b/src/terrain.c
new file mode 100644 (file)
index 0000000..5b6d48a
--- /dev/null
@@ -0,0 +1,269 @@
+#include <math.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "terrain.h"
+
+static void delete_chunk(TerrainChunk *chunk, Terrain *terrain)
+{
+       if (terrain->callbacks.delete_chunk)
+               terrain->callbacks.delete_chunk(chunk);
+
+       terrain_free_chunk(chunk);
+}
+
+static void delete_sector(TerrainSector *sector, Terrain *terrain)
+{
+       tree_clr(&sector->chunks, (void *) &delete_chunk, terrain, NULL, 0);
+       pthread_rwlock_destroy(&sector->lock);
+       free(sector);
+}
+
+Terrain *terrain_create()
+{
+       Terrain *terrain = malloc(sizeof *terrain);
+       tree_ini(&terrain->sectors);
+       pthread_rwlock_init(&terrain->lock, NULL);
+       terrain->cache = NULL;
+       pthread_rwlock_init(&terrain->cache_lock, NULL);
+       return terrain;
+}
+
+void terrain_delete(Terrain *terrain)
+{
+       tree_clr(&terrain->sectors, (void *) &delete_sector, terrain, NULL, 0);
+       pthread_rwlock_destroy(&terrain->lock);
+       pthread_rwlock_destroy(&terrain->cache_lock);
+       free(terrain);
+}
+
+TerrainSector *terrain_get_sector(Terrain *terrain, v2s32 pos, bool create)
+{
+       if (create)
+               pthread_rwlock_wrlock(&terrain->lock);
+       else
+               pthread_rwlock_rdlock(&terrain->lock);
+
+       TreeNode **loc = tree_nfd(&terrain->sectors, &pos, &v2s32_cmp);
+       TerrainSector *sector = NULL;
+
+       if (*loc) {
+               sector = (*loc)->dat;
+       } else if (create) {
+               sector = malloc(sizeof *sector);
+               sector->pos = pos;
+               tree_ini(&sector->chunks);
+               pthread_rwlock_init(&sector->lock, NULL);
+
+               tree_nmk(&terrain->sectors, loc, sector);
+       }
+
+       pthread_rwlock_unlock(&terrain->lock);
+
+       return sector;
+}
+
+TerrainChunk *terrain_get_chunk(Terrain *terrain, v3s32 pos, bool create)
+{
+       TerrainChunk *cache = NULL;
+
+       pthread_rwlock_rdlock(&terrain->cache_lock);
+       cache = terrain->cache;
+       pthread_rwlock_unlock(&terrain->cache_lock);
+
+       if (cache && v3s32_equals(cache->pos, pos))
+               return cache;
+
+       TerrainSector *sector = terrain_get_sector(terrain, (v2s32) {pos.x, pos.z}, create);
+       if (!sector)
+               return NULL;
+
+       if (create)
+               pthread_rwlock_wrlock(&sector->lock);
+       else
+               pthread_rwlock_rdlock(&sector->lock);
+
+       TreeNode **loc = tree_nfd(&sector->chunks, &pos.y, &s32_cmp);
+       TerrainChunk *chunk = NULL;
+
+       if (*loc) {
+               chunk = (*loc)->dat;
+
+               pthread_mutex_lock(&chunk->mtx);
+               if (terrain->callbacks.get_chunk && !terrain->callbacks.get_chunk(chunk, create)) {
+                       pthread_mutex_unlock(&chunk->mtx);
+                       chunk = NULL;
+               } else {
+                       pthread_mutex_unlock(&chunk->mtx);
+                       pthread_rwlock_wrlock(&terrain->cache_lock);
+                       terrain->cache = chunk;
+                       pthread_rwlock_unlock(&terrain->cache_lock);
+               }
+       } else if (create) {
+               tree_nmk(&sector->chunks, loc, chunk = terrain_allocate_chunk(pos));
+
+               if (terrain->callbacks.create_chunk)
+                       terrain->callbacks.create_chunk(chunk);
+       }
+
+       pthread_rwlock_unlock(&sector->lock);
+
+       return chunk;
+}
+
+TerrainChunk *terrain_allocate_chunk(v3s32 pos)
+{
+       TerrainChunk *chunk = malloc(sizeof * chunk);
+       chunk->level = pos.y;
+       chunk->pos = pos;
+       chunk->extra = NULL;
+       pthread_mutexattr_t attr;
+       pthread_mutexattr_init(&attr);
+       pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
+       pthread_mutex_init(&chunk->mtx, &attr);
+
+       CHUNK_ITERATE
+               chunk->data[x][y][z] = terrain_node_create(NODE_UNKNOWN, (Blob) {0, NULL});
+
+       return chunk;
+}
+
+void terrain_free_chunk(TerrainChunk *chunk)
+{
+       CHUNK_ITERATE
+               terrain_node_delete(chunk->data[x][y][z]);
+
+       pthread_mutex_destroy(&chunk->mtx);
+       free(chunk);
+}
+
+Blob terrain_serialize_chunk(TerrainChunk *chunk)
+{
+       bool empty = true;
+
+       CHUNK_ITERATE {
+               if (chunk->data[x][y][z].type != NODE_AIR) {
+                       empty = false;
+                       break;
+               }
+       }
+
+       if (empty)
+               return (Blob) {0, NULL};
+
+       SerializedTerrainChunk chunk_data;
+
+       CHUNK_ITERATE {
+               TerrainNode *node = &chunk->data[x][y][z];
+               SerializedTerrainNode *node_data = &chunk_data.raw.nodes[x][y][z];
+
+               *node_data = (SerializedTerrainNode) {
+                       .type = node->type,
+                       .data = {
+                               .siz = 0,
+                               .data = NULL,
+                       },
+               };
+
+               NodeDefinition *def = &node_definitions[node->type];
+
+               if (def->serialize)
+                       def->serialize(&node_data->data, node->data);
+       }
+
+       Blob buffer = {0, NULL};
+       SerializedTerrainChunk_write(&buffer, &chunk_data);
+       SerializedTerrainChunk_free(&chunk_data);
+
+       return buffer;
+}
+
+bool terrain_deserialize_chunk(TerrainChunk *chunk, Blob buffer)
+{
+       if (buffer.siz == 0) {
+               CHUNK_ITERATE
+                       chunk->data[x][y][z] = terrain_node_create(NODE_AIR, (Blob) {0, NULL});
+
+               return true;
+       }
+
+       // it's important to copy Blobs that have been malloc'd before reading from them
+       // because reading from a Blob modifies its data and size pointer,
+       // but does not free anything
+       SerializedTerrainChunk chunk_data = {0};
+       bool success = SerializedTerrainChunk_read(&buffer, &chunk_data);
+
+       if (success) CHUNK_ITERATE
+               chunk->data[x][y][z] = terrain_node_create(chunk_data.raw.nodes[x][y][z].type, chunk_data.raw.nodes[x][y][z].data);
+
+       SerializedTerrainChunk_free(&chunk_data);
+       return success;
+}
+
+v3s32 terrain_node_to_chunk_pos(v3s32 pos, v3u8 *offset)
+{
+       if (offset)
+               *offset = (v3u8) {(u32) pos.x % CHUNK_SIZE, (u32) pos.y % CHUNK_SIZE, (u32) pos.z % CHUNK_SIZE};
+       return (v3s32) {floor((double) pos.x / (double) CHUNK_SIZE), floor((double) pos.y / (double) CHUNK_SIZE), floor((double) pos.z / (double) CHUNK_SIZE)};
+}
+
+TerrainNode terrain_get_node(Terrain *terrain, v3s32 pos)
+{
+       v3u8 offset;
+       v3s32 chunkpos = terrain_node_to_chunk_pos(pos, &offset);
+       TerrainChunk *chunk = terrain_get_chunk(terrain, chunkpos, false);
+       if (!chunk)
+               return terrain_node_create(NODE_UNLOADED, (Blob) {0, NULL});
+       return chunk->data[offset.x][offset.y][offset.z];
+}
+
+void terrain_set_node(Terrain *terrain, v3s32 pos, TerrainNode node, bool create, void *arg)
+{
+       v3u8 offset;
+       TerrainChunk *chunk = terrain_get_chunk(terrain, terrain_node_to_chunk_pos(pos, &offset), create);
+
+       if (!chunk)
+               return;
+
+       pthread_mutex_lock(&chunk->mtx);
+       if (!terrain->callbacks.set_node || terrain->callbacks.set_node(chunk, offset, &node, arg)) {
+               chunk->data[offset.x][offset.y][offset.z] = node;
+               if (terrain->callbacks.after_set_node)
+                       terrain->callbacks.after_set_node(chunk, offset, arg);
+       } else {
+               terrain_node_delete(node);
+       }
+       pthread_mutex_unlock(&chunk->mtx);
+}
+
+TerrainNode terrain_node_create(NodeType type, Blob buffer)
+{
+       if (type >= NODE_UNLOADED)
+               type = NODE_UNKNOWN;
+
+       NodeDefinition *def = &node_definitions[type];
+
+       TerrainNode node;
+       node.type = type;
+       node.data = def->data_size ? malloc(def->data_size) : NULL;
+
+       if (def->create)
+               def->create(&node);
+
+       if (def->deserialize)
+               def->deserialize(&buffer, node.data);
+
+       return node;
+}
+
+void terrain_node_delete(TerrainNode node)
+{
+       NodeDefinition *def = &node_definitions[node.type];
+
+       if (def->delete)
+               def->delete(&node);
+
+       if (node.data)
+               free(node.data);
+}
diff --git a/src/terrain.h b/src/terrain.h
new file mode 100644 (file)
index 0000000..111e175
--- /dev/null
@@ -0,0 +1,72 @@
+#ifndef _TERRAIN_H_
+#define _TERRAIN_H_
+
+#include <dragonstd/list.h>
+#include <dragonstd/tree.h>
+#include <stdbool.h>
+#include <pthread.h>
+#include "node.h"
+#include "types.h"
+
+#define CHUNK_ITERATE \
+       for (u8 x = 0; x < CHUNK_SIZE; x++) \
+       for (u8 y = 0; y < CHUNK_SIZE; y++) \
+       for (u8 z = 0; z < CHUNK_SIZE; z++)
+
+typedef struct TerrainNode {
+       NodeType type;
+       void *data;
+} TerrainNode;
+
+typedef TerrainNode TerrainChunkData[CHUNK_SIZE][CHUNK_SIZE][CHUNK_SIZE];
+
+typedef struct {
+       s32 level;
+       v3s32 pos;
+       TerrainChunkData data;
+       void *extra;
+       pthread_mutex_t mtx;
+} TerrainChunk;
+
+typedef struct
+{
+       v2s32 pos;
+       Tree chunks;
+       pthread_rwlock_t lock;
+} TerrainSector;
+
+typedef struct {
+       Tree sectors;
+       pthread_rwlock_t lock;
+       TerrainChunk *cache;
+       pthread_rwlock_t cache_lock;
+       struct {
+               void (*create_chunk)(TerrainChunk *chunk);
+               void (*delete_chunk)(TerrainChunk *chunk);
+               bool (*get_chunk)(TerrainChunk *chunk, bool create);
+               bool (*set_node) (TerrainChunk *chunk, v3u8 offset, TerrainNode *node, void *arg);
+               void (*after_set_node)(TerrainChunk *chunk, v3u8 offset, void *arg);
+       } callbacks;
+} Terrain;
+
+Terrain *terrain_create();
+void terrain_delete(Terrain *terrain);
+
+TerrainSector *terrain_get_sector(Terrain *terrain, v2s32 pos, bool create);
+TerrainChunk *terrain_get_chunk(Terrain *terrain, v3s32 pos, bool create);
+
+TerrainChunk *terrain_allocate_chunk(v3s32 pos);
+void terrain_free_chunk(TerrainChunk *chunk);
+
+Blob terrain_serialize_chunk(TerrainChunk *chunk);
+bool terrain_deserialize_chunk(TerrainChunk *chunk, Blob buffer);
+
+v3s32 terrain_node_to_chunk_pos(v3s32 pos, v3u8 *offset);
+
+TerrainNode terrain_get_node(Terrain *terrain, v3s32 pos);
+void terrain_set_node(Terrain *terrain, v3s32 pos, TerrainNode node, bool create, void *arg);
+
+TerrainNode terrain_node_create(NodeType type, Blob buffer);
+void terrain_node_delete(TerrainNode node);
+
+#endif
index 4a82d9c245f8a8561bc2f30cf234ce5ded3bc91d..9aa6aa710989b42e2df4704e4adffa6441ad0346 100644 (file)
@@ -1,23 +1,33 @@
-#define MAPBLOCK_SIZE 16
+#define CHUNK_SIZE 16
 
-HSLData
+ColorData
        v3f32 color
 
-SerializedMapNode
+SerializedTerrainNode
        u32 type
        Blob data
 
-SerializedMapBlockRaw
-       SerializedMapNode[MAPBLOCK_SIZE][MAPBLOCK_SIZE][MAPBLOCK_SIZE] nodes
+SerializedTerrainChunkRaw
+       SerializedTerrainNode[CHUNK_SIZE][CHUNK_SIZE][CHUNK_SIZE] nodes
 
-SerializedMapBlock
-       compressed SerializedMapBlockRaw raw
+SerializedTerrainChunk
+       compressed SerializedTerrainChunkRaw raw
 
-MapgenStageBufferRaw
-       u32[MAPBLOCK_SIZE][MAPBLOCK_SIZE][MAPBLOCK_SIZE] nodes
+TerrainGenStageBufferRaw
+       u32[CHUNK_SIZE][CHUNK_SIZE][CHUNK_SIZE] nodes
 
-MapgenStageBuffer
-       compressed MapgenStageBufferRaw raw
+TerrainGenStageBuffer
+       compressed TerrainGenStageBufferRaw raw
+
+EntityData
+       u64 id
+       v3f64 pos
+       v3f32 rot
+       aabb3f32 box
+       v3f32 eye
+       String nametag
+
+; server packets
 
 pkt ToServerAuth
        String name
@@ -26,25 +36,57 @@ pkt ToServerSetnode
        v3s32 pos
        u32 node
 
-pkt ToServerPos
+pkt ToServerPosRot
        v3f64 pos
+       v3f32 rot
 
-pkt ToServerRequestBlock
+pkt ToServerRequestChunk
        v3s32 pos
 
+pkt ToServerRequestMovement
+       u8 flight
+       u8 collision
+
+; client packets
+
 pkt ToClientAuth
        u8 success
 
-pkt ToClientBlock
+pkt ToClientChunk
        v3s32 pos
        Blob data
 
 pkt ToClientInfo
-       u32 simulation_distance
+       u32 load_distance
        s32 seed
 
-pkt ToClientPos
-       v3f64 pos
-
 pkt ToClientTimeOfDay
        u64 time_of_day
+
+pkt ToClientMovement
+       u8 flight
+       u8 collision
+       f32 speed
+       f32 jump
+       f32 gravity
+
+pkt ToClientEntityAdd
+       u32 type
+       EntityData data
+
+pkt ToClientEntityRemove
+       u64 id
+
+pkt ToClientEntityUpdatePosRot
+       u64 id
+       v3f64 pos
+       v3f32 rot
+
+pkt ToClientEntityUpdateBoxEye
+       u64 id
+       aabb3f32 box
+       v3f32 eye
+
+pkt ToClientEntityUpdateNametag
+       u64 id
+       String nametag
diff --git a/src/util.c b/src/util.c
deleted file mode 100644 (file)
index 436e4d9..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-#include <stdarg.h>
-#include <dragonport/asprintf.h>
-
-char *format_string(const char *format, ...)
-{
-       va_list args;
-       va_start(args, format);
-       char *ptr;
-       vasprintf(&ptr, format, args);
-       va_end(args);
-       return ptr;
-}
diff --git a/src/util.h b/src/util.h
deleted file mode 100644 (file)
index 64e7287..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-#ifndef _UTIL_H_
-#define _UTIL_H_
-
-#define CMPBOUNDS(x) ((x) == 0 ? 0 : (x) > 0 ? 1 : -1)                       // resolves to 1 if x > 0, 0 if x == 0 and -1 if x < 0
-#define fallthrough __attribute__ ((fallthrough))                            // prevent compiler warning about implicit fallthrough with style
-#define unused __attribute__ ((unused))
-#define U32(x) (((u32) 1 << 31) + (x))
-
-char *format_string(const char *format, ...);
-
-#endif
diff --git a/textures/models/player/arm/back.png b/textures/models/player/arm/back.png
new file mode 100644 (file)
index 0000000..6b98546
Binary files /dev/null and b/textures/models/player/arm/back.png differ
diff --git a/textures/models/player/arm/bottom.png b/textures/models/player/arm/bottom.png
new file mode 100644 (file)
index 0000000..ceaa6aa
Binary files /dev/null and b/textures/models/player/arm/bottom.png differ
diff --git a/textures/models/player/arm/front.png b/textures/models/player/arm/front.png
new file mode 100644 (file)
index 0000000..f62ee88
Binary files /dev/null and b/textures/models/player/arm/front.png differ
diff --git a/textures/models/player/arm/left.png b/textures/models/player/arm/left.png
new file mode 100644 (file)
index 0000000..558391a
Binary files /dev/null and b/textures/models/player/arm/left.png differ
diff --git a/textures/models/player/arm/right.png b/textures/models/player/arm/right.png
new file mode 100644 (file)
index 0000000..b1b7b9f
Binary files /dev/null and b/textures/models/player/arm/right.png differ
diff --git a/textures/models/player/arm/top.png b/textures/models/player/arm/top.png
new file mode 100644 (file)
index 0000000..45567ca
Binary files /dev/null and b/textures/models/player/arm/top.png differ
diff --git a/textures/models/player/chest/back.png b/textures/models/player/chest/back.png
new file mode 100644 (file)
index 0000000..0a0a862
Binary files /dev/null and b/textures/models/player/chest/back.png differ
diff --git a/textures/models/player/chest/bottom.png b/textures/models/player/chest/bottom.png
new file mode 100644 (file)
index 0000000..e51749a
Binary files /dev/null and b/textures/models/player/chest/bottom.png differ
diff --git a/textures/models/player/chest/front.png b/textures/models/player/chest/front.png
new file mode 100644 (file)
index 0000000..6ec46e1
Binary files /dev/null and b/textures/models/player/chest/front.png differ
diff --git a/textures/models/player/chest/left.png b/textures/models/player/chest/left.png
new file mode 100644 (file)
index 0000000..c04fb71
Binary files /dev/null and b/textures/models/player/chest/left.png differ
diff --git a/textures/models/player/chest/right.png b/textures/models/player/chest/right.png
new file mode 100644 (file)
index 0000000..60329f4
Binary files /dev/null and b/textures/models/player/chest/right.png differ
diff --git a/textures/models/player/chest/top.png b/textures/models/player/chest/top.png
new file mode 100644 (file)
index 0000000..2711dd1
Binary files /dev/null and b/textures/models/player/chest/top.png differ
diff --git a/textures/models/player/head/back.png b/textures/models/player/head/back.png
new file mode 100644 (file)
index 0000000..6b98546
Binary files /dev/null and b/textures/models/player/head/back.png differ
diff --git a/textures/models/player/head/bottom.png b/textures/models/player/head/bottom.png
new file mode 100644 (file)
index 0000000..ceaa6aa
Binary files /dev/null and b/textures/models/player/head/bottom.png differ
diff --git a/textures/models/player/head/front.png b/textures/models/player/head/front.png
new file mode 100644 (file)
index 0000000..f62ee88
Binary files /dev/null and b/textures/models/player/head/front.png differ
diff --git a/textures/models/player/head/left.png b/textures/models/player/head/left.png
new file mode 100644 (file)
index 0000000..558391a
Binary files /dev/null and b/textures/models/player/head/left.png differ
diff --git a/textures/models/player/head/right.png b/textures/models/player/head/right.png
new file mode 100644 (file)
index 0000000..b1b7b9f
Binary files /dev/null and b/textures/models/player/head/right.png differ
diff --git a/textures/models/player/head/top.png b/textures/models/player/head/top.png
new file mode 100644 (file)
index 0000000..45567ca
Binary files /dev/null and b/textures/models/player/head/top.png differ
diff --git a/textures/models/player/leg/back.png b/textures/models/player/leg/back.png
new file mode 100644 (file)
index 0000000..6b98546
Binary files /dev/null and b/textures/models/player/leg/back.png differ
diff --git a/textures/models/player/leg/bottom.png b/textures/models/player/leg/bottom.png
new file mode 100644 (file)
index 0000000..ceaa6aa
Binary files /dev/null and b/textures/models/player/leg/bottom.png differ
diff --git a/textures/models/player/leg/front.png b/textures/models/player/leg/front.png
new file mode 100644 (file)
index 0000000..f62ee88
Binary files /dev/null and b/textures/models/player/leg/front.png differ
diff --git a/textures/models/player/leg/left.png b/textures/models/player/leg/left.png
new file mode 100644 (file)
index 0000000..558391a
Binary files /dev/null and b/textures/models/player/leg/left.png differ
diff --git a/textures/models/player/leg/right.png b/textures/models/player/leg/right.png
new file mode 100644 (file)
index 0000000..b1b7b9f
Binary files /dev/null and b/textures/models/player/leg/right.png differ
diff --git a/textures/models/player/leg/top.png b/textures/models/player/leg/top.png
new file mode 100644 (file)
index 0000000..45567ca
Binary files /dev/null and b/textures/models/player/leg/top.png differ
index 55cb954aeadff37497b70ecf6f553053ffbb1ef4..90af189e08f91a0ae25d9336a58c755313368dad 100644 (file)
Binary files a/textures/oak_wood.png and b/textures/oak_wood.png differ
index c9a8e6c762fa7f5ef00f1f8a5dd0c19848486d21..fa554b9853f19a633b267c040f16b54ece40ccee 100755 (executable)
--- a/upload.sh
+++ b/upload.sh
@@ -1,4 +1,4 @@
-#! /bin/bash
+#!/bin/bash
 VERSION=`git tag --points-at HEAD`
 IS_RELEASE="1"
 if [[ $VERSION = "" ]]; then
@@ -9,5 +9,5 @@ curl -f -i -X POST -H "Content-Type: multipart/form-data" \
        -F "secret=$SECRET" \
        -F "name=$VERSION" \
        -F "is_release=$IS_RELEASE" \
-       -F "build=@DragonblocksAlpha-$VERSION.zip" \
+       -F "build=@dragonblocks_alpha-$VERSION.zip" \
        https://elidragon.tk/dragonblocks_alpha/upload.php