1
0
Fork 0

Compare commits

...

4 Commits

Author SHA1 Message Date
May B. 7f8e249a18 Basic gravity using pseudo sphere collider 2020-11-14 20:53:52 +01:00
May B. 96f4bbf11c Add icon 2020-11-14 14:05:00 +01:00
May B. b6bd32ebdb Fix: OpenGL version 2020-11-12 21:55:38 +01:00
May B. 82cf0daa75 Gitlab CI: generic packages 2020-11-12 21:43:13 +01:00
29 changed files with 949 additions and 229 deletions

View File

@ -1,29 +1,58 @@
image: gcc
stages:
- build
- deploy
before_script:
- apt-get update
- apt-get install -y --no-install-recommends cmake libssl-dev libvulkan-dev xorg-dev doxygen graphviz
build:
build:linux:
stage: build
image: gcc
before_script:
- apt-get update
- apt-get install -y --no-install-recommends cmake libssl-dev libvulkan-dev xorg-dev doxygen graphviz
script:
- mkdir build # compile
- mkdir build
- cd build
- cmake ..
- make -j2 univerxel univerxel-client univerxel-server docs
- mkdir -p ../out/full ../out/client ../out/server/content # package artifacts
- cp -r univerxel content ../out/full
- cp -r univerxel-client content ../out/client
- rm ../out/client/content/cert.pem ../out/client/content/key.pem ../out/client/content/zstd.dict
- cp univerxel-server ../out/server
- cp content/cert.pem content/key.pem content/zstd.dict ../out/server/content
dependencies: []
artifacts:
paths:
- out/full
- out/client
- out/server
- build/content
- build/univerxel
- build/univerxel-client
- build/univerxel-server
- build/docs
expire_in: 1 week
# cache:
# paths:
# - "*.o"
deploy:package:
stage: deploy
image: curlimages/curl
script:
- mkdir out # package
- cd build
- mkdir -p applications
- cp ../resource/config/*.desktop applications
- mkdir -p icons/hicolor/256x256/apps
- cp ../univerxel.png icons/hicolor/256x256/apps
- cp ../resource/config/full.toml config.toml
- tar cfz ../out/full-nux64.tar.gz univerxel applications/fr.wadza.univerxel.desktop config.toml icons content
- cp ../resource/config/client.toml config.toml
- tar cfz ../out/client-nux64.tar.gz univerxel-client applications/fr.wadza.univerxel.client.desktop config.toml icons content/shaders content/textures
- cp ../resource/config/server.toml config.toml
- tar cfz ../out/server-nux64.tar.gz univerxel-server applications/fr.wadza.univerxel.server.desktop config.toml icons content/cert.pem content/key.pem content/zstd.dict
- cd ../out # deploy
- 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file full-nux64.tar.gz ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/Univerxel/$CI_COMMIT_TAG/Univerxel-$CI_COMMIT_TAG-nux64.tar.gz'
- 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file client-nux64.tar.gz ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/Univerxel-client/$CI_COMMIT_TAG/Univerxel-client-$CI_COMMIT_TAG-nux64.tar.gz'
- 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file server-nux64.tar.gz ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/Univerxel-server/$CI_COMMIT_TAG/Univerxel-server-$CI_COMMIT_TAG-nux64.tar.gz'
dependencies:
- build:linux
artifacts:
paths:
- out
expire_in: 1 week
except:
- branches
only:
- /\A\d+\.\d+\.\d+\z/

View File

@ -8,7 +8,6 @@ option(USE_FMA "Use fma" 1)
option(LOG_DEBUG "Show debug logs" 0)
option(LOG_TRACE "Show trace logs" 0)
option(NATIVE "Build with -march=native" 0)
option(GL_OLD "Use OpenGL 4.2 not 4.6" 0)
find_program(CCACHE_FOUND ccache)
if(CCACHE_FOUND)
@ -24,7 +23,7 @@ if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release)
endif()
if(WIN32)
if(MSVC)
add_definitions(/std:c++latest)
add_compile_definitions(WIN32_LEAN_AND_MEAN=)
else()
@ -32,7 +31,7 @@ else()
endif()
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
add_compile_definitions(FIXED_WINDOW=${FIXED_WINDOW} LOG_DEBUG=${LOG_DEBUG} LOG_TRACE=${LOG_TRACE} GL_OLD=${GL_OLD} HN_USE_FILESYSTEM=1)
add_compile_definitions(FIXED_WINDOW=${FIXED_WINDOW} LOG_DEBUG=${LOG_DEBUG} LOG_TRACE=${LOG_TRACE} HN_USE_FILESYSTEM=1)
if(PROFILING)
add_compile_definitions(TRACY_ENABLE=1)
endif(PROFILING)
@ -44,13 +43,13 @@ if(SIMD_LEVEL EQUAL "avx2")
elseif(SIMD_LEVEL EQUAL "avx512f")
add_compile_definitions(FN_COMPILE_AVX512=1)
endif()
if(WIN32)
if(MSVC)
add_definitions(/arch:AVX2)
else()
add_definitions(-m${SIMD_LEVEL})
endif()
if(USE_FMA)
if(WIN32)
if(MSVC)
add_definitions(/GL /fp:fast)
else()
add_definitions(-mfma)
@ -62,7 +61,7 @@ set(CORE_HEADERS "deps/toml++" "deps/robin_hood" "deps/libguarded" "deps/tracy")
set(CORE_LIBS glm::glm_static zstd::zstd_static) # picoquic
file(GLOB_RECURSE CLIENT_SOURCES "src/client/*.cpp" "deps/imgui/*.cpp" "deps/meshoptimizer/*.cpp" "deps/gl3w/gl3w.c" "deps/volk/volk.c")
set(CLIENT_HEADERS "deps/imgui" "deps/meshoptimizer" "deps/gl3w" "deps/volk") # vulkan
set(CLIENT_HEADERS "deps/imgui" "deps/meshoptimizer" "deps/gl3w" "deps/volk")
set(CLIENT_LIBS glfw)
file(GLOB_RECURSE SERVER_SOURCES "src/server/*.cpp" "deps/FastNoiseSIMD/*.cpp")
@ -77,20 +76,26 @@ else()
set(CORE_LIBS ${CORE_LIBS} picoquic-core pthread dl)
endif()
if (WIN32)
set(ICON univerxel.rc)
elseif (APPLE)
set(ICON univerxel.icns)
endif()
# All in one exec
add_executable(univerxel "src/main.cpp" ${CORE_SOURCES} ${CLIENT_SOURCES} ${SERVER_SOURCES})
add_executable(univerxel "src/main.cpp" ${ICON} ${CORE_SOURCES} ${CLIENT_SOURCES} ${SERVER_SOURCES})
target_compile_features(univerxel PUBLIC cxx_std_17)
target_link_libraries(univerxel ${CORE_LIBS} ${CLIENT_LIBS} ${SERVER_LIBS})
target_include_directories(univerxel PRIVATE ${CORE_HEADERS} ${CLIENT_HEADERS} ${SERVER_HEADERS})
# Standalone server
add_executable(univerxel-server EXCLUDE_FROM_ALL "src/server.cpp" ${CORE_SOURCES} ${SERVER_SOURCES})
add_executable(univerxel-server EXCLUDE_FROM_ALL "src/server.cpp" ${ICON} ${CORE_SOURCES} ${SERVER_SOURCES})
target_compile_features(univerxel-server PUBLIC cxx_std_17)
target_link_libraries(univerxel-server ${CORE_LIBS} ${SERVER_LIBS})
target_include_directories(univerxel-server PRIVATE ${CORE_HEADERS} ${SERVER_HEADERS})
# Dumb client
add_executable(univerxel-client EXCLUDE_FROM_ALL "src/client.cpp" ${CORE_SOURCES} ${CLIENT_SOURCES})
add_executable(univerxel-client EXCLUDE_FROM_ALL "src/client.cpp" ${ICON} ${CORE_SOURCES} ${CLIENT_SOURCES})
target_compile_features(univerxel-client PUBLIC cxx_std_17)
target_link_libraries(univerxel-client ${CORE_LIBS} ${CLIENT_LIBS})
target_include_directories(univerxel-client PRIVATE ${CORE_HEADERS} ${CLIENT_HEADERS})
@ -111,3 +116,15 @@ add_custom_target(docs
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
COMMENT "Build doc."
)
if (APPLE)
set_target_properties(univerxel PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Univerxel")
set_target_properties(univerxel-client PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Univerxel Client")
set_target_properties(univerxel-server PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Univerxel Server")
set_source_files_properties(univerxel.icns PROPERTIES MACOSX_PACKAGE_LOCATION "Resources")
set_target_properties(univerxel univerxel-client PROPERTIES
MACOSX_BUNDLE_SHORT_VERSION_STRING ${GLFW_VERSION}
MACOSX_BUNDLE_LONG_VERSION_STRING ${GLFW_VERSION}
MACOSX_BUNDLE_ICON_FILE univerxel.icns
MACOSX_BUNDLE_INFO_PLIST "resource/config/Info.plist.in")
endif()

View File

@ -103,7 +103,6 @@ PROFILING | Tracy profiling | `0`
LOG_DEBUG | Debug logs | `0`
LOG_TRACE | Trace logs | `0`
NATIVE | Optimize for native CPU | `0`
GL_OLD | Use OpenGL 4.2 not 4.6 | `0`
1. Build Make
```sh

View File

@ -43,12 +43,14 @@
## Hello universe
- [~] CI build
- [ ] CMake package
- [x] CI build
- [x] CMake package
- [x] GitLab CI
- Platfoms
- [x] Linux
- [~] Windows
- [x] Windows
- Mingw: requires picotls, picoquic and FastNoise compatibility
- [ ] MacOS
- [ ] Universe
- [ ] Galaxy
- [ ] Rotation

View File

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleExecutable</key>
<string>${MACOSX_BUNDLE_EXECUTABLE_NAME}</string>
<key>CFBundleGetInfoString</key>
<string>${MACOSX_BUNDLE_INFO_STRING}</string>
<key>CFBundleIconFile</key>
<string>${MACOSX_BUNDLE_ICON_FILE}</string>
<key>CFBundleIdentifier</key>
<string>${MACOSX_BUNDLE_GUI_IDENTIFIER}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleLongVersionString</key>
<string>${MACOSX_BUNDLE_LONG_VERSION_STRING}</string>
<key>CFBundleName</key>
<string>${MACOSX_BUNDLE_BUNDLE_NAME}</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>${MACOSX_BUNDLE_BUNDLE_VERSION}</string>
<key>CSResourcesFileMapped</key>
<true/>
<key>LSRequiresCarbon</key>
<true/>
<key>NSHumanReadableCopyright</key>
<string>${MACOSX_BUNDLE_COPYRIGHT}</string>
<key>NSHighResolutionCapable</key>
<true/>
</dict>
</plist>

85
resource/config/client.toml Executable file
View File

@ -0,0 +1,85 @@
contouring = '''iso = 0.1000000014901161
keep_distance = 10
load_distance = 9
lod_levels = [ false, true, false, true, false ]
lod_quality = 0.0
lod_strength = 0.1500000059604645
manifold = true
reordering = true
transparency = false
'''
enabled = true
[camera]
far = 272.0
fov = 1.221730589866638
near = 0.1000000014901161
[connection]
host = '127.0.0.1'
port = 4242
[console]
opacity = 0.800000011920929
scroll = true
visible = true
[control]
collide = true
sensibility = 50
speed = 25.0
[debug_menu]
bar = true
contouring = false
controls = false
render = true
world = false
[editor]
visible = false
[editor.tool]
empty_air = true
material = 5
radius = 2
shape = 0
[overlay]
corner = 3
visible = true
[render]
blend = true
culling = 0
curvature = true
curvature_depth = true
fog = true
fog_color = '#0000'
geometry = true
pbr = true
prefer_vulkan = false
skybox = true
stochastic = false
texture_angular_quality = 0
texture_quality = 100
textures = '1024-realistic'
transparency = false
triplanar = false
[window]
fullscreen = false
parallel_frames = 2
sampling = -1
target_fps = 23
[world]
edit_handling = true
edit_prediction = true
keep_distance = 12
load_distance = 11
trust_majorant = true
use_averages = false
voxel_density = 1

View File

@ -0,0 +1,11 @@
[Desktop Entry]
Name=Univerxel Client
TryExec=univerxel-client
Exec=univerxel-client %u
Terminal=true
Type=Application
Icon=univerxel
Categories=Game;Network;
Keywords=game;online;
StartupNotify=true
Actions=Composer;

View File

@ -0,0 +1,11 @@
[Desktop Entry]
Name=Univerxel
TryExec=univerxel
Exec=univerxel %u
Terminal=true
Type=Application
Icon=univerxel
Categories=Game;Network;
Keywords=game;online;
StartupNotify=true
Actions=Composer;

View File

@ -0,0 +1,11 @@
[Desktop Entry]
Name=Univerxel Server
TryExec=univerxel-server
Exec=univerxel-server %u
Terminal=true
Type=Application
Icon=univerxel
Categories=Game;Network;
Keywords=game;online;
StartupNotify=true
Actions=Composer;

102
resource/config/full.toml Executable file
View File

@ -0,0 +1,102 @@
[client]
contouring = '''iso = 0.1000000014901161
keep_distance = 10
load_distance = 9
lod_levels = [ false, true, false, true, false ]
lod_quality = 0.0
lod_strength = 0.1500000059604645
manifold = true
reordering = true
transparency = false
'''
enabled = true
[client.camera]
far = 272.0
fov = 1.221730589866638
near = 0.1000000014901161
[client.connection]
use_local = true
[client.console]
opacity = 0.800000011920929
scroll = true
visible = true
[client.control]
collide = true
sensibility = 50
speed = 25.0
[client.debug_menu]
bar = true
contouring = false
controls = false
render = true
world = false
[client.editor]
visible = false
[client.editor.tool]
empty_air = true
material = 5
radius = 2
shape = 0
[client.overlay]
corner = 3
visible = true
[client.render]
blend = true
culling = 0
curvature = true
curvature_depth = true
fog = true
fog_color = '#0000'
geometry = true
pbr = true
prefer_vulkan = false
skybox = true
stochastic = false
texture_angular_quality = 0
texture_quality = 100
textures = '1024-realistic'
transparency = false
triplanar = false
[client.window]
fullscreen = false
parallel_frames = 2
sampling = -1
target_fps = 23
[client.world]
edit_handling = true
edit_prediction = true
keep_distance = 12
load_distance = 11
trust_majorant = true
use_averages = false
voxel_density = 1
[server]
allow_local = true
enabled = true
max_players = 1
[server.connection]
cert = 'content/cert.pem'
hosts = ''
key = 'content/key.pem'
port = 4242
[server.world]
keep_distance = 6
load_distance = 5
max_part_size = 32768
path = 'world'

16
resource/config/server.toml Executable file
View File

@ -0,0 +1,16 @@
enabled = true
max_players = 10
[connection]
cert = 'content/cert.pem'
hosts = ''
key = 'content/key.pem'
port = 4242
[world]
keep_distance = 12
load_distance = 10
max_part_size = 32768
path = 'world'

View File

@ -6,6 +6,7 @@
#include <thread>
#include <chrono>
#include "../core/utils/logger.hpp"
#include "logo.cxx"
/// Default floating window width
constexpr auto DEFAULT_WIDTH = 1280;
@ -84,6 +85,7 @@ bool Window::create(const CreateInfo &opt) {
glfwSetFramebufferSizeCallback(ptr, opt.pfnResize);
glfwSetWindowTitle(ptr, APP_NAME);
glfwSetWindowIcon(ptr, 1, &LOGO);
glfwSetWindowAttrib(ptr, GLFW_RESIZABLE, true);
setTargetFPS(opt.fps);

View File

@ -68,6 +68,8 @@ public:
world.trustMajorant = config["world"]["trust_majorant"].value_or(world.trustMajorant);
world.editPrediction = config["world"]["edit_prediction"].value_or(world.editPrediction);
world.editHandling = config["world"]["edit_handling"].value_or(world.editHandling);
world.movePrediction = config["world"]["move_prediction"].value_or(world.movePrediction);
world.moveCollision = config["world"]["move_collision"].value_or(world.moveCollision);
voxel_density = config["world"]["voxel_density"].value_or(voxel_density);
#ifndef STANDALONE
@ -148,6 +150,8 @@ public:
{"trust_majorant", world.trustMajorant},
{"edit_prediction", world.editPrediction},
{"edit_handling", world.editHandling},
{"move_prediction", world.movePrediction},
{"move_collision", world.moveCollision},
{"voxel_density", voxel_density}
}));
if(connection.has_value()) {

297
src/client/logo.cxx Normal file
View File

@ -0,0 +1,297 @@
static const unsigned char LOGO_DATA[48 * 48 * 4 + 1] =
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\025\020\037(\034\026'\362\010\005\031\375\011\006\033\375\007\004"
"\025\362%\037\060)\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\023\016#I\027"
"\022(\377\013\010\035\377\033\026,\377\012\007\033\377\011\006\032J\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000:)b\000\026\017>\034\030\021;\217gJu%\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\020\014$g\017\013%\377\024\017.\377\021\015$\377\017\013"
"!\377\017\013!i\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\022\014%%\025"
"\020%\217\061)B\034\010\005\024\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\002\001\010\000M\071\206\007\060$iz\034\025"
"J\363\033\024@\377\032\022?\313\023\015\071\010\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\034\024\066\205\023\015.\377\036\026\066\377\016\012#\377\013\007\037\377\025\017"
"%\206\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\024\017&\010\022\015'\312\012\007\035"
"\377\012\007\035\363\012\007\036{\010\006\033\007\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000</\212\000\065*|\231/$g\377\036\027"
"J\377.&N\377wn\231\377)!T\223\040\026G\000\000\000\000\000\000\000\000\000\012\010#\000\012\010\"\013"
"\014\011(\254\034\024\066\377\022\015-\377\061'C\377\062(?\377\011\007\040\255\023\013\066"
"\013\017\013\064\000\000\000\000\000\000\000\000\000<$Z\000\030\021-\222\015\011%\377\015\012!\377\016\013"
"$\377\023\016$\377\010\006\035\231\011\004\035\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\060%uL\"\032\\\377\035\027I\377"
"!\032J\377\034\026I\377\031\023>\375\020\013\062U\015\011,\063\014\011*\205\024\017\061\312"
"\027\021-\365\027\021/\377\015\011(\377\033\025\062\377\030\021.\377\016\011#\377\022\016"
"(\377\024\016\062\365\025\020\071\312\021\015.\206\015\012*\063\070/NT\030\021;\375+$B"
"\377\025\020/\377\022\015\061\377\013\010%\377\012\007!M\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\037\030\\\004"
"&\035T\332$\034O\377$\034O\377\027\023D\377\037\030E\377\017\012/\373\023\016\065\376"
"\030\022\065\377K<a\377\025\016\063\377\016\013*\377\033\023\064\377\031\021\064\377D\064"
"S\377\032\025-\377\016\012$\377\035\026\064\377\023\015\065\377\017\013\060\377\022\015"
",\376E\071^\373$\034D\377)\037C\377\031\022>\377\060%c\377\035\026I\332\014\011'\004"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\003\000\030\023Jp\027\022E\377+\036N\377\"\031L\377!\031G\377\031"
"\022:\377\022\015\065\377\023\016\060\377#\034=\377\020\014\061\377\021\014/\377\020\013"
",\377\022\014\060\377\020\014&\377\023\016'\377\013\010#\377\013\010$\377%\032;\377\015"
"\012*\377\031\023\065\377\024\016\070\377\022\016\067\377!\031@\377\037\027I\377\276\264"
"\334\377|j\260p\001\001\006\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000&\033U\025\033\024G\370&\034M\377"
"'\037R\377(\037P\377\024\016\071\377/\"A\377\036\030\065\377)!@\377\032\023\070\377"
"\020\014.\377\012\010\040\377\034\023,\377\013\010!\377\016\012#\377\032\024*\377\031\024"
".\377\017\012,\377\026\020/\377\030\022\070\377\023\016\070\377\026\022;\377\031\023\067"
"\377\063'X\377O?|\371dR\237\025\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\001\000\016\010+\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\033\023M\015+!`\265\033"
"\023J\377\036\026J\377\030\022G\377D\065_\377.&M\377:-R\377\023\016\061\377\021\015"
"/\377\022\015\062\377\015\012+\376-\"<\363\014\010\037\351\010\006\032\351\010\005\031\363"
"\011\007\036\376!\032\070\377%\035:\377\021\015'\377\025\017\066\377\031\022;\377\026\021"
":\377\024\015\066\377\040\030B\377\027\017\067\377\027\020-\265\017\011+\015\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\007\005\023\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\015\011,\007\020\013\061\231\031\022>M\014\012\065\004\003\002\007\000\000"
"\000\000\000/\"Z\015$\033R\302TG\177\377cY\207\377$\034Q\377\033\024D\377\"\030C\377"
"\035\026B\377$\033?\377\016\012-\362\016\013\060\243)\035DW\015\011.$\012\007#\037\034\027"
".'\007\005\031'\021\013$\037\011\007\037$\017\012/W\015\011%\243\025\017*\362\021\014\063\377"
"\025\016<\377J>e\377(\034F\377\023\016\066\377\021\014/\377)\036=\377\017\011,\303\020"
"\012.\015\000\000\000\000\000\000\001\000\026\020\064\004\017\011$M\026\017!\231\005\003\026\007\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\017\010-\000\016\013(y\031\024\065\377.\040F\377\031"
"\023@\332\032\023Hq\037\026P\025\034\025L\264$\034P\377)\036W\377\036\026P\377\060$Q\377"
"\037\030E\377)\036K\377>(]\361\026\020<~\021\014\061\036\017\013\061_\020\014\062\261\020"
"\014\060\352\031\022\061\374\027\020'\377\016\012\"\377\020\014&\374\017\013*\352\013\010"
"$\261\011\007\037`\014\011(\036\021\014\067~\033\023F\361*\037M\377\025\017<\377\040\031@"
"\377\036\026?\377\024\015\061\377\021\013/\377\037\025<\265\022\013\060\025\026\016\064p"
"\017\012)\332\017\012&\377\021\014\036\377\017\012\036{\010\005\027\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\015\012+\033\032\024\063\362\031\022\063\377\027\021;\377\037\027M\377"
")\036S\377\070(Y\371\035\027L\377\036\027H\377!\030K\377\027\021@\377\030\021C\377,"
"\037L\377.\040O\275\237|\255&\032\023=q\040\031;\351\026\021\066\377\034\025\071\377"
"\021\014\062\354\033\026\071\304\040\030\070\256\035\027\070\255\021\015(\304\027\021\060"
"\354\012\007\037\377\033\024+\377\013\010#\351\016\012.r\037\027J%<\060`\275\034\025I\377"
"\033\023I\377(\034O\377\037\027<\377\037\027:\377\034\023<\377\037\030=\371WHp\377\024"
"\014/\377\030\021.\377\040\027.\377\034\023)\363\017\012\040\034\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\022\015\064\216\023\016\063\377\021\014\063\377\030\021A\377&\034X\377$"
"\034P\377\031\022D\377!\031N\377\037\031H\377\026\020@\377\031\022C\377\033\024C\377"
"\031\023@\226\034\023L(\040\030P\311\027\021;\377\022\015\062\366\032\023\070\232!\032;"
"\071\017\015\064\005\017\013\061\000\000\000\000\000\000\000\000\000\013\006\040\000\012\007#\005\011\007\036\071\023"
"\016(\231\030\023\061\365\021\014\065\377\030\021B\312\030\021D(!\027R\226%\033N\377&"
"\033Q\377&\033F\377!\027<\377\025\016\064\377\031\022:\377\032\021;\377!\027\067\377"
"\021\013.\377#\033\067\377\015\010\037\377\031\022&\217\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\025\020<%\023\016\071\312\033\024;\377\026\020@\377\032\023F\377\032\024E\377\036"
"\027H\377#\032O\377\036\026K\377\035\026K\377\031\024G\377\030\022D\226\032\024K\063a"
"R\205\350!\031O\377\021\015\065\266\021\014\065\"\016\011/\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\014\007&\000\017\012.!.&L\265*\037T\377"
"%\035U\350\036\026P\064\037\026L\226\034\024J\377\037\027D\377\062&N\377\034\023\071\377"
"\031\022\067\377$\030B\377I\065X\377\030\021\064\377\035\025\067\377\024\015)\313\012\006"
"\032%\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\021\014\064\010\024\016\071\222(\035"
"G\375\033\024D\377#\034G\377\033\024C\377&\035Q\377-\040Z\377\037\027L\377=-[\275"
"=\061](\033\024E\350&\034[\376\036\027R\203\023\016;\003\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\030"
"\023D\003\035\025K\202#\032U\376\031\023G\350\037\026P(\035\024J\275-#N\377\031\022=\377"
"!\030A\377$\033E\377#\030C\377#\032?\377\060\"E\375XDe\223lW\205\010\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\017\010\063\000#\034BT\022\016\070"
"\373\026\021A\377\025\020@\377(\035S\377*\037]\377(\036Z\361O\067e&#\032N\311&\035"
"T\377&\035T\203!\027T\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\"\030U\000C\061"
"g\203\062$S\377\060!_\312\037\027R%%\033O\361&\033K\377.\037P\377\033\024I\377\040"
"\030P\377,!U\373\036\024JU\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\021\014\064\063\023\016:\376\030\023H\377\034\025J\377"
".\"Y\377G\071r\377g[\217~\036\026Sq?\060g\377.\"_\266K\071p\003\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\"\020[\003\037\027Q\265/\"f\377\062&o"
"r%\033\\~%\033W\377G\065i\377\032\023H\377%\032W\377<*b\376F/m\064\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\026\021"
"B\205\026\021A\377\033\026I\377\037\031N\377+\"V\377,![\362'\034`\036<-s\351/$p\366"
"~i\236\"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000$\033^!=+q\365\071+s\351\062$j\037&\034^\362-\040_\377!\030V\377$\032U\377"
"\061!U\377\064\040`\206\000\000\001\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\027\023F\000\027\022C\312\027\022E\377#\034Q\377</c\377\035\026"
"O\377\"\032P\244\062%l_:,s\377/%q\232\061't\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000)\040e\000+!i\231\070*v\377,\"j_\060"
"$p\243;-r\377)\037d\377-!`\377A\062g\377+\037U\313\"\025Q\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\031\022G\013\035\027G\365\036\026"
"F\377\034\025K\377\065(a\377'\036Y\377\031\024JX.#l\261\065)w\377\062's:\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\067*y\070>\061\200\377;.}\261\067+{W<-v\377\071+o\377-\"g\377(\034\\\377"
"%\032X\365\037\026P\013\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\006\004\022(\021\015&I\024\016"
"\065g\026\021@\206\034\025L\254!\030V\377\035\025M\377\060'X\377=\061j\377,\040]\376"
"\037\027U$/$i\352D\065\201\355\066+{\006\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\070,}\005=\061\205\354;.\202"
"\352\067+|$@\061\204\376p`\237\377\067)w\377\066'q\377\065'o\377\060$e\255I\064"
"k\206&\032Rh\066(YJ</Q(\012\007\030\361\015\011\040\377\024\016\064\377\034\025K\377'\035"
"\\\377n]\217\377%\034W\377!\031Y\377'\035b\377/$i\363*\040g\037.#h\374=\060|\304"
"I\070\211\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000F\066\222\000N?\234\304@\062\206\374\067+|\037\071-|\363"
"\060$o\377@\060{\377Q@\207\377<,w\377;*v\377\071(m\377\071)j\377\062#Z\377\061"
"%L\361\027\020\061\375\021\013$\377\014\010&\377\033\024F\377'\034V\377#\032W\377!\030"
"U\377$\033[\377TD\202\377zj\246\351)\040f'+\"h\377/$n\256\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000TD\243\256M>\227\377F\066\215';/\200\351\067*y\377F\065\203\377"
"<-\177\377?.\201\377\066'u\377:)w\377\065't\377F\061q\377%\032S\374\027\020\065"
"\375\024\016\060\377-#F\377+\037M\377\036\026O\377\036\026S\377C\067j\377'\034\\\377"
"\067+o\377<.}\351+!j',!g\377A\060t\256\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000`M\256"
"\256ZH\251\377PB\236'I;\221\351VC\226\377B\062\207\377B\062\205\377C\061\205"
"\377A\061\177\377O=\203\377J\067\177\377;,z\377;+q\374\030\021\066\361\025\017:"
"\377\032\022E\377!\030O\377\037\026P\377\062%[\377\040\027O\377\071)i\377\063's\377"
"\070+z\363\070*z\037/$n\374)\037e\304*\040h\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000bK\266\000u]\302\304"
"\232\202\320\374cN\266\037\\J\256\363SB\237\377K;\227\377M\071\217\377>-\177"
"\377\061$n\377,\037g\377>.o\377]L\217\377D\061\204\361aSs(\027\020>I\033\022Fh!"
"\027R\206!\030Q\255\033\024K\377\062'^\377.\"h\377Q<\201\377A.z\376\071*{$\063'"
"t\352+\040g\355\060\040i\006\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000jR\300\005w_\305\354rY\301\352TE\247"
"$VE\250\376UD\245\377_J\252\377Q?\226\377N;\210\377\061#m\255+\035c\206-\040"
"_g\064%pIK\067\217(\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000!\026S\013'\033]\365+\037^\377"
"/#g\377R>\202\377@.x\377A/|X\061%n\261+\037e\377\061!j:\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000jQ\273"
"\071t]\310\377nV\301\261hS\273WbN\262\377]J\255\377YF\243\377B\060\205\377"
"=+}\365=+~\013\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000;(j\000-\037c\312,\037a\377\064'k\377\062$p\377\062$n\377\061#l\244/!h_\060!i"
"\377\071&r\232A,|\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000WA\245\000VA\243\231~f\302\377\214t\321_\206n\322\243qZ\303"
"\377`K\260\377R?\232\377<+s\377,\035`\312/\036h\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\001\000+\034\\\205)\033]\377\062#g\377"
"\064$m\377\064%m\377.\036c\362\061\040g\036\070%n\351\067#k\366\063\035`\"\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000eJ\263!j"
"O\262\365~c\304\351\205o\323\037\200j\317\362nV\300\377^I\257\377I\066\220"
"\377E\065y\377M>u\206\000\000\001\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000#\026Q\063!\024L\376\"\024L\377)\031S\377-\034]\377"
"\063!d\377\071$m~\065\040gq\061\035`\377.\030S\266\060\030K\003\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000|a\307\003sY\301\265y\\\302\377\245\213"
"\324r\223|\331~\210q\322\377sY\267\377VA\234\377E\062\210\377>+x\376-\034b"
"\064\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000*\032V\000%\027QT#\025M\373\031\017<\377\036\021=\377)\027I\377*\030T\377R?y\362\063"
"\034b&\062\034`\311;#f\377\060\031V\203(\017;\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\202c\307\000\225w\323\203\204i\315\377}b\311\311\216u\325&\221y\326"
"\361\204k\315\377lS\263\377R;\225\377A,\201\377@+x\373<,lU\000\000C\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000G<U\010\024\013\061\222\"\025"
"K\375\037\023F\377\022\012,\377\020\007!\377\033\015\063\377'\025L\377/\033]\377\066\037"
"a\276\070\040g'F,x\350=%p\376.\027R\203\070\040d\003\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\241"
"\200\331\003\225t\323\203\231|\331\376\225{\327\350\231\201\331(\244\213\340"
"\275\244\214\337\377\211o\321\377gM\262\377R;\226\377C-\200\377\070%q\377"
"\063!f\375<(m\223%\024P\011\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\020\010'%"
"\025\015)\312\017\010$\377\020\010(\377\033\017<\377\020\007$\377\011\004\022\377\014\005\030"
"\377\036\017\065\377-\030P\377\061\027U\377:\036f\227O\062\210\063G,x\350\071\037c\377"
":\034[\266\070\031V\"B\037h\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\212^\303\000\225k\304\"\235x\324\265\217l\315\377\230y\326"
"\350\235\201\334\063\231y\322\226\247\214\337\377\244\207\333\377\211m\316"
"\377rV\275\377ZA\241\377aK\225\377\061\037d\377\062\"Z\377+\034X\377\060\036Y\313"
"&\027Q%\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\014\006\034\216\031\021+\377\024\014\061\377"
"\016\007#\377\017\007\040\377\013\004\024\377\012\004\021\377\015\005\030\377\027\012*\377*\023"
"H\377:\034`\377H*w\377K-~\227?%r(F*w\311\071\035\\\377<\032Y\366fC\206\232R/"
"\202\071Y\066\221\005Y\065\220\000\000\000\000\000\000\000\000\000oF\246\000\210^\303\005\201V\273\071"
"\200T\270\231vN\252\366\203Z\272\377\200Z\275\311\210c\306(\253\211\334\226"
"\234}\321\377xZ\260\377\234\177\321\377\220u\320\377eK\255\377S;\230\377"
"F\060}\377\067#f\377\066#c\377:(f\377\064\"^\377P\064f\217\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\021\012+\033)\037<\362\030\015\067\377\035\022\066\377\017\006\034\377\010"
"\004\016\377\014\005\025\371\025\010%\377\036\014\062\377+\021C\377P\062o\377O,|\377L*"
"|\377N,\201\276Z\066\217&>!hq;\033T\351K\"h\377S+|\377`;\227\355b<\227\304"
"jB\242\256c\071\226\256k<\231\304\201U\265\354oC\243\377mB\242\377c\071\223"
"\351mB\235r\205[\267&\242|\326\275\255\215\340\377\252\211\335\377\235}\327"
"\377\230}\323\377\204j\306\377[B\237\377A*}\371;%p\377\064\037g\377\062\036a"
"\377\064\"b\377)\032Y\363&\030R\034\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\025\015\063"
"\000\022\012-z\025\014\062\377\023\011)\377\016\006\032\332\007\004\014q\013\005\023\025#\016\063"
"\264,\022A\377<\031T\377M\"i\377Y,\177\377P'v\377U,\200\377f;\225\361a\066\223"
"~V,\177\036D\035[_T's\261c\065\220\352l@\231\374k?\234\377n?\232\377\260\177"
"\276\374i\071\217\352b\065\216\261uG\252_\223h\306\036\207b\266~uK\250\361\230"
"l\311\377\236x\324\377\235w\322\377\211g\303\377L\061}\377cI\250\377S<\230"
"\265H/\200\025A(pp\062\034a\331\060\034]\377.\035Y\377/\034V{\"\030T\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\013\006\034\007\012\005\026\231\015\005\031M\026\011&\004\000"
"\000\000\000\000\000\000\000*\021>\015;\031U\302d\065\201\377yB\233\377t?\240\377Z*z\377r\070"
"\225\377\206L\257\377\200E\253\377u;\234\362h\061\212\244|=\237Xx\071\225$"
"\201L\245\037j;\220'i\067\224'\221U\256\037o;\230$i\067\224W\215Z\275\243\245"
"u\323\362\204Z\262\377\214\\\275\377\207U\265\377\204T\266\377\214b\301\377"
"nK\245\377B(_\377;%h\303[A\240\015\000\000\000\000\001\000\004\000/\030Z\004-\030\\L)\030Y\231"
"(\026S\007\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\007\004\017\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000Y*~\015\206L\257\265\215O\270\377"
"\202D\254\377w:\227\377\216M\265\377\237a\302\377\243b\302\377\232S\271\377"
"\221N\266\377\210D\251\377s\065\216\376{;\231\363u:\230\351\177J\254\351~"
"I\257\363r>\237\376t?\240\377\216Z\275\377\264\213\327\377\234k\313\377\217"
"[\275\377\205J\257\377zC\244\377vF\245\377lF\241\377R\061{\265\017\006\027\015"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000+\032\\\000\001\000\003\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\225Q\274\025\230W\276\370\231V\276\377\241c\300\377"
"\237^\301\377\247i\306\377\260q\312\377\245d\300\377\240[\276\377\230W\272"
"\377\232Y\273\377\223P\264\377q\066\215\377p\070\222\377{D\240\377t;\230\377"
"s:\227\377\213P\265\377\223^\277\377\226b\305\377\222\\\275\377\204I\255"
"\377\203P\250\377o>\233\377i?\232\371Z\064\215\025\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\014\003\020\000\236W\275p\242`\303\377\247d\304\377\262r\312\377\273\200\321\377"
"\257o\307\377\267{\315\377\274\202\326\377\257r\313\377\276\206\330\377\257"
"u\312\377\227S\260\377n\062\202\377Y&k\377\177O\224\377h/\201\377g\062}\377"
"\221S\266\377\232^\300\377\230^\300\377\232d\301\377\220Q\270\377}A\244\377"
"r?\236\377c\065\220\377zV\233p\023\013\027\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\243W\272\004\243Y"
"\272\331\247e\303\377\252h\304\377\271z\317\377\305\211\331\377\266w\314"
"\373\246b\276\376\256r\312\377\264x\317\377\271\177\323\377\255q\311\377"
"\240]\274\377\205B\237\377o\062\202\377](p\377j/}\377p\064\212\377\215H\260"
"\377\214F\255\377\226U\272\376\226\\\276\373\212J\262\377\215K\261\377\212"
"K\261\377d\062\215\377X/~\332M$t\004\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\250c\275L\247]\271\377\247"
"b\276\377\262r\312\377\272{\317\377\312\230\334\375\337\300\352U\250d\301"
"\063\261t\315\205\261t\314\312\255s\313\365\242b\300\377\244c\303\377\223"
"Q\257\377\227R\262\377\216N\246\377\235c\263\377\213K\243\365\222N\264\312"
"\206A\244\206\225O\266\063\217M\267T\205G\255\375z>\242\377\202A\247\377\202"
"H\237\377[\060~\377M%uM\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\246^\273\000\253h\300\231\251b\274\377\246Z"
"\270\377\263t\312\377\265u\313\377\260p\307\223\315\214\332\000\000\000\000\000\006\002"
"\010\000\243a\301\000\254o\310\013\302\214\332\254\315\233\342\377\317\233\342"
"\377\313\227\337\377\264y\316\377\271\207\320\255\306\230\321\013\222L\265"
"\000\004\001\006\000\000\000\000\000\216L\266\000z=\237\222k\061\213\377q\067\224\377{B\227\377"
"h<\204\377Y\063z\231J#p\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\013\003\016\000\256o\305\007\252d\275z\247]\271\363\257"
"o\306\377\264r\310\313\257h\301\010\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\313\227\343\205\316\232\346\377\305\216\337\377\305\216\337\377\304\216"
"\335\377\272\201\326\206\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000n\062\217"
"\010g.\201\312r\066\224\377w\071\225\363f.\207zS%v\007\002\001\005\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\246`\271\000\247b\275\034\253k\301\217\265u\312%\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\312\227\344g\312\225\343\377\303\215\340\377\304\217"
"\340\377\311\224\340\377\307\221\336h\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000g.\201$q\063\216\217p\064\220\034z\070\226\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\323\245\347I\323\242\350\377\316\233\345\377\310\224"
"\341\377\311\224\340\377\310\222\336I\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\322\242\350'\322\243\350\361\312\230\344\375\275\207\333\375"
"\253n\307\361\257t\312(\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000";
static const struct GLFWimage LOGO = {48, 48, (unsigned char*)LOGO_DATA};

View File

@ -12,11 +12,8 @@
using namespace render::gl;
constexpr auto GL_MAJOR = 4;
#if GL_OLD
constexpr auto GL_MINOR = 2;
#else
constexpr auto GL_MINOR = 6;
#endif
constexpr auto GL_MINOR_MIN = 2;
constexpr auto GL_MINOR_BEST = 6;
#define CONTENT_DIR "content/"
#define TEXTURES_DIR CONTENT_DIR "textures/"
@ -50,7 +47,7 @@ void framebuffer_size_callback(GLFWwindow *, int width, int height) {
bool Renderer::Load(Window& window, const render::renderOptions& opt, const windowOptions& windOpt) {
Window::CreateInfo windowInfo;
windowInfo.pfnResize = framebuffer_size_callback;
windowInfo.client = {Window::CreateInfo::Client::Type::GL, GL_MAJOR, GL_MINOR};
windowInfo.client = {Window::CreateInfo::Client::Type::GL, GL_MAJOR, GL_MINOR_MIN};
windowInfo.samples = windOpt.getSamples();
windowInfo.fps = windOpt.targetFPS;
windowInfo.fullscreen = windOpt.fullscreen;
@ -62,9 +59,13 @@ bool Renderer::Load(Window& window, const render::renderOptions& opt, const wind
LOG_E("Failed to initialize OpenGL");
return false;
}
if (!gl3wIsSupported(GL_MAJOR, GL_MINOR)) {
LOG_E("OpenGL " << GL_MAJOR << "." << GL_MINOR << " not supported");
return false;
if (!gl3wIsSupported(GL_MAJOR, GL_MINOR_BEST)) {
if (gl3wIsSupported(GL_MAJOR, GL_MINOR_MIN)) {
LOG_W("OpenGL " << GL_MAJOR << "." << GL_MINOR_BEST << " not supported. Some feature like texture sharpness may not work");
} else {
LOG_E("OpenGL " << GL_MAJOR << "." << GL_MINOR_MIN << " not supported");
return false;
}
}
LOG_D("OpenGL " << glGetString(GL_VERSION) << ", GLSL " << glGetString(GL_SHADING_LANGUAGE_VERSION));

View File

@ -76,9 +76,8 @@ void applySampler(GLuint textureID, const Texture::sampling& props) {
glTextureParameteri(textureID, GL_TEXTURE_MAG_FILTER, getFilter(props.magLinear, props.mipmap));
glTextureParameteri(textureID, GL_TEXTURE_MIN_FILTER, getFilter(props.minLinear, props.mipmap));
glTextureParameterf(textureID, GL_TEXTURE_LOD_BIAS, props.mipmapLod);
#if !GL_OLD
glTextureParameterf(textureID, GL_TEXTURE_MAX_ANISOTROPY, props.anisotropy);
#endif
glTextureParameteri(textureID, GL_TEXTURE_WRAP_S, wrap);
glTextureParameteri(textureID, GL_TEXTURE_WRAP_T, wrap);
glTextureParameteri(textureID, GL_TEXTURE_WRAP_R, wrap);

View File

@ -128,6 +128,19 @@ void DistantUniverse::update(voxel_pos pos, float deltaTime) {
}
}
}
if (options.movePrediction) {
entities.remove([&](size_t eId, Entity &entity) {
entity.instances.remove([&](size_t iId, Universe::Entity::Instance &inst) {
if (eId == PLAYER_ENTITY_ID.index && iId == playerId)
return false;
// MAYBE: leap after entities packet
inst.pos += inst.velocity * deltaTime;
return false;
});
return false;
});
}
contouring->update(pos, areas);
}
@ -172,7 +185,7 @@ bool DistantUniverse::onPacket(const data::out_view& buf, net::PacketFlags) {
}
case server_packet_type::COMPRESSION: {
const auto remain = packet.readAll();
const auto remain = packet.readRemaining();
dict.emplace(remain.data(), remain.size());
LOG_T("Compression dictionnary loaded");
mayQueryChunks = true;
@ -187,13 +200,13 @@ bool DistantUniverse::onPacket(const data::out_view& buf, net::PacketFlags) {
}
case server_packet_type::MESSAGE: {
const auto remain = packet.readAll();
const auto remain = packet.readRemaining();
onMessage(std::string((const char*)remain.data(), remain.size()));
break;
}
case server_packet_type::AREAS: {
while(!packet.isFull()) {
while(!packet.isDone()) {
area_id id;
world::Area::params p;
if(!packet.read(id) || !packet.read(p))
@ -228,7 +241,7 @@ bool DistantUniverse::onPacket(const data::out_view& buf, net::PacketFlags) {
// MAYBE: then create virtual or non chunk of single material
uint16_t full = 0;
uint16_t total = 0;
while (!packet.isFull()) {
while (!packet.isDone()) {
region_chunk_pos cpos;
Voxel voxel;
if (!packet.read(cpos) || !packet.read(voxel))
@ -247,7 +260,7 @@ bool DistantUniverse::onPacket(const data::out_view& buf, net::PacketFlags) {
if (!dict.has_value())
break;
if (packet.isFull()) {
if (packet.isDone()) {
mayQueryChunks = true;
break;
}
@ -267,7 +280,7 @@ bool DistantUniverse::onPacket(const data::out_view& buf, net::PacketFlags) {
}
std::vector<char> buffer;
if(auto err = dict.value().decompress(packet.readAll(), buffer)) {
if(auto err = dict.value().decompress(packet.readRemaining(), buffer)) {
LOG_E("Corrupted chunk packet " << err.value());
break;
}
@ -313,14 +326,14 @@ bool DistantUniverse::onPacket(const data::out_view& buf, net::PacketFlags) {
break;
}
while(!packet.isFull()) {
while(!packet.isDone()) {
chunk_pos pos = chunk_pos(INT_MAX);
packet.read(pos);
chunk_voxel_idx count = 0;
packet.read(count);
if (auto ptr = it->second->setChunks().findInRange(pos)) {
auto chunk = std::dynamic_pointer_cast<Chunk>(ptr.value());
for (auto i = 0; i < count && !packet.isFull(); i++) {
for (auto i = 0; i < count && !packet.isDone(); i++) {
Chunk::Edit edit;
packet.read(edit);
chunk->apply(edit);
@ -334,7 +347,7 @@ bool DistantUniverse::onPacket(const data::out_view& buf, net::PacketFlags) {
case server_packet_type::ENTITY_TYPES: {
entities.clear();
while(!packet.isFull()) {
while(!packet.isDone()) {
size_t index;
glm::usvec3 size;
glm::vec3 scale;
@ -351,7 +364,7 @@ bool DistantUniverse::onPacket(const data::out_view& buf, net::PacketFlags) {
case server_packet_type::ENTITIES: {
entities.remove([](size_t, Entity &entity) { entity.instances.clear(); return false; });
while(!packet.isFull()) {
while(!packet.isDone()) {
size_t entity_idx;
size_t count;
if (!(packet.read(entity_idx) && packet.read(count)))
@ -360,7 +373,7 @@ bool DistantUniverse::onPacket(const data::out_view& buf, net::PacketFlags) {
if (auto entity = entities.get(entity_idx)) {
if (count == 0)
continue;
for (size_t i = 0; i < count && !packet.isFull(); i++) {
for (size_t i = 0; i < count && !packet.isDone(); i++) {
size_t idx;
glm::ifvec3 pos;
glm::vec3 vel;
@ -410,15 +423,9 @@ bool DistantUniverse::onPacket(const data::out_view& buf, net::PacketFlags) {
auto area = std::get_if<Universe::Entity::area_t>(&it->shape);
assert(area);
area->clear();
glm::i64 i = 0;
while (!packet.isFull()) {
i++;
size_t size = 0;
if (!packet.read(size))
break;
while (!packet.isDone()) {
std::vector<char> buffer;
if(auto err = dict.value().decompress(packet.readPart(size), buffer)) {
if(auto err = dict.value().decompress(packet.readSizedPart(), buffer)) {
LOG_E("Corrupted entity chunk packet " << err.value());
break;
}
@ -427,15 +434,15 @@ bool DistantUniverse::onPacket(const data::out_view& buf, net::PacketFlags) {
area->push_back(std::make_shared<Chunk>(iss));
}
auto scale = glm::lvec3(1) + glm::divide(it->size);
if (i != scale.x * scale.y * scale.z) {
LOG_E("Corrupted entity area");
if ((long)area->size() != scale.x * scale.y * scale.z) {
LOG_E("Corrupted entity area " << area->size() << "!=" << (scale.x * scale.y * scale.z));
break;
}
contouring->onEntityLoad(id, scale, *area);
} else {
auto model = std::get_if<Universe::Entity::model_t>(&it->shape);
assert(model);
auto remain = packet.readAll();
auto remain = packet.readRemaining();
model->resize(remain.size());
//MAYBE: avoid storing model
memcpy(model->data(), remain.data(), remain.size());

View File

@ -29,6 +29,10 @@ namespace world::client {
bool editPrediction = true;
/// Get only edits commands and compute locally
bool editHandling = true;
/// Apply velocity locally. Smooth entities positions
bool movePrediction = true;
/// Check for entities collisions
bool moveCollision = false;
};
struct connection: net::address {
connection(): net::address{"127.0.0.1", 4242} { }

View File

@ -42,6 +42,7 @@ namespace glm {
}
inline ifvec3 operator+(const offset_t& v) const { return ifvec3(raw, offset + v); }
inline ifvec3 operator-(const offset_t &v) const { return ifvec3(raw, offset - v); }
inline ifvec3 operator-(const ifvec3 &v) const { return ifvec3(raw - v.raw, offset - v.offset); }
inline ifvec3 operator/(int i) const { return ifvec3(raw / i, offset / (i * 1.f), false); }
inline ifvec3 operator*(int i) const { return ifvec3(raw * i, offset * (i * 1.f)); }
};

View File

@ -76,7 +76,8 @@ namespace glm {
constexpr T inline max_axis(const vec<3, T> &v) {
return std::max({v.x, v.y, v.z});
}
ivec3 inline inormalize(const ivec3 &v) {
template <typename T>
ivec3 inline inormalize(const vec<3, T> &v) {
const auto a = glm::abs(v);
return a.x >= a.y ? (a.x >= a.z ? ivec3(glm::sign(v.x), 0, 0) : ivec3(0, 0, glm::sign(v.z))) :
(a.y >= a.z ? ivec3(0, glm::sign(v.y), 0) : ivec3(0, 0, glm::sign(v.z)));

View File

@ -24,6 +24,131 @@ struct view {
constexpr size_t size() const { return siz; }
constexpr bool isDone() const { return cur >= siz; }
};
// Abstract data writer
struct in_view: view {
in_view(uint8_t *ptr, size_t size): view(size), ptr(ptr) {
assert(ptr != nullptr);
}
uint8_t* ptr;
uint8_t* writeTo(size_t len) {
assert(cur + len <= siz);
auto p = ptr + cur;
cur += len;
return p;
}
void write(const uint8_t* data, size_t len) {
memcpy(writeTo(len), data, len);
}
};
/// Writer with Dynamic allocation and subparts
struct in_writer: public in_view {
private:
static constexpr auto EMPTY_OFFSET = 0x1000;
static uint8_t* alloc(size_t size) { return (uint8_t*)(size > 0 ? ::malloc(size) : reinterpret_cast<uint8_t*>(EMPTY_OFFSET)); }
static uint8_t* realloc(uint8_t* ptr, size_t size) { return (uint8_t*)::realloc(ptr, size); }
static void free(uint8_t* ptr) { ::free(ptr); }
inline void auto_size(size_t len) {
if (cur + len > siz)
resize(cur + len);
}
public:
in_writer(size_t init_size = 0): in_view(alloc(init_size), init_size), vis_siz(init_size) { }
~in_writer() { if (ptr != reinterpret_cast<uint8_t*>(EMPTY_OFFSET)) free(ptr); }
size_t vis_siz = 0;
uint8_t* data() { return ptr; }
constexpr size_t size() const { return vis_siz; }
void write(const void* data, size_t len) {
in_view::write((uint8_t*)data, len);
}
/// Write without resize
template<typename D>
void write(const D& d) {
write(&d, sizeof(d));
}
uint8_t* pushTo(size_t len) {
auto_size(len);
return writeTo(len);
}
void push(const void* data, size_t len) {
auto_size(len);
in_view::write((uint8_t*)data, len);
}
template<typename D>
void push(const D& d) {
push(&d, sizeof(d));
}
size_t getCursor() const { return cur; }
void writeAt(size_t pos, const void* data, size_t len) {
assert(pos + len <= vis_siz);
memcpy(ptr + pos, data, len);
}
template<typename D>
void writeAt(size_t pos, const D& d) {
writeAt(pos, &d, sizeof(d));
}
void reserve(size_t target) {
if (target >= siz) {
ptr = realloc(ptr, target);
siz = target;
}
}
void resize(size_t target) {
reserve(target);
vis_siz = target;
}
bool isFull() const {
return cur >= vis_siz;
}
// Must take ptr ownership before
void reset() {
ptr = reinterpret_cast<uint8_t*>(EMPTY_OFFSET);
cur = 0;
siz = 0;
vis_siz = 0;
}
struct part {
part(in_writer* parent): up(parent), start(parent->cur) { }
in_writer* up;
size_t start;
uint8_t* data() { return up->data() + start; }
constexpr size_t size() const { return up->size() - start; }
void reserve(size_t size) {
up->reserve(start + size);
}
void resize(size_t size) {
up->resize(start + size);
}
};
template<typename CB>
size_t pushPart(const CB& callback) {
const auto prev = cur;
callback(part(this));
cur = vis_siz;
return cur - prev;
}
template<typename CB>
size_t pushSizedPart(const CB& callback) {
const auto size_cur = cur;
push<size_t>(0);
const size_t size = pushPart(callback);
writeAt(size_cur, size);
return size;
}
};
/// Vector with in_view interface
struct in_vector {
/// 512 Mébibits
@ -44,23 +169,6 @@ struct in_vector {
memcpy(writeTo(len), data, len);
}
};
// Abstract data writer
struct in_view: view {
in_view(uint8_t *ptr, size_t size): view(size), ptr(ptr) {
assert(ptr != nullptr);
}
uint8_t* ptr;
uint8_t* writeTo(size_t len) {
assert(cur + len <= siz);
auto p = ptr + cur;
cur += len;
return p;
}
void write(const uint8_t* data, size_t len) {
memcpy(writeTo(len), data, len);
}
};
/// Abstract data reader
struct out_view: view {
out_view(): out_view(nullptr, 0) { }
@ -80,6 +188,42 @@ struct out_view: view {
constexpr const uint8_t* data() const { return ptr; }
constexpr size_t remaining() const { return size() - cur; }
};
/// Helper to read from out_view
struct out_reader: public out_view {
out_reader(const out_view& buf): out_view(buf.ptr, buf.siz) { cur = buf.cur; }
template<typename D>
const D* read() {
return cur + sizeof(D) <= size() ?
(const D*)readFrom(sizeof(D)) : nullptr;
}
template<typename D>
bool read(D& out) {
const auto ptr = read<D>();
if (ptr == nullptr)
return false;
out = *ptr;
return true;
}
bool skip(size_t size) {
readFrom(size);
return !isDone();
}
data::out_view readPart(size_t size) {
return data::out_view(readFrom(size), size);
}
data::out_view readSizedPart() {
size_t size = 0;
read(size);
return readPart(size);
}
data::out_view readRemaining() {
return readPart(remaining());
}
};
/// Pointer to opaque owned memory
using handle_t = std::shared_ptr<const void>;
/// out_view with owned data
@ -92,6 +236,9 @@ struct out_buffer: out_view {
out_buffer(view, std::shared_ptr<const void>(view.ptr)) { }
/// Take ptr ownership
out_buffer(const uint8_t *ptr, size_t size): out_buffer(out_view(ptr, size)) { }
/// Take ptr ownership (release writer)
out_buffer(in_writer& writer):
out_buffer(writer.data(), writer.size()) { writer.reset(); }
handle_t handle = nullptr;
};

View File

@ -8,100 +8,19 @@ namespace net {
//TODO: preallocate static data pool
/// Helper to write allocated packets
class PacketWriter final {
class PacketWriter final: public data::in_writer {
public:
PacketWriter(size_t size): buffer((uint8_t*)malloc(size), size), visible_size(size) { }
PacketWriter(size_t size): data::in_writer(size) { }
PacketWriter(uint8_t type, size_t data_size): PacketWriter(sizeof(type) + data_size) {
write(type);
}
PacketWriter(net::client_packet_type type, size_t data_size): PacketWriter((uint8_t)type, data_size) { }
PacketWriter(net::server_packet_type type, size_t data_size): PacketWriter((uint8_t)type, data_size) { }
~PacketWriter() {
if (buffer.ptr != nullptr)
free(buffer.ptr);
}
void write(const void* data, size_t len) {
buffer.write((uint8_t*)data, len);
}
template<typename D>
void write(const D& d) {
write(&d, sizeof(d));
}
constexpr size_t getCursor() const { return buffer.cur; }
void writeAt(size_t pos, const void* data, size_t len) {
assert(pos + len <= buffer.siz);
memcpy(buffer.ptr + pos, data, len);
}
template<typename D>
void writeAt(size_t pos, const D& d) {
writeAt(pos, &d, sizeof(d));
}
void reserve(size_t target) {
if (target >= buffer.siz - buffer.cur) {
const auto size = target + buffer.cur;
buffer.ptr = (uint8_t *)realloc(buffer.ptr, size);
buffer.siz = size;
}
}
void resize(size_t target) {
reserve(target);
visible_size = target;
}
void writePush(const void* data, size_t len) {
if (buffer.cur + len > buffer.siz)
resize(buffer.cur + len);
write(data, len);
}
template<typename D>
void writePush(const D& d) {
writePush(&d, sizeof(d));
}
struct varying_part {
varying_part(data::in_view &buffer, size_t& p_size): buffer(buffer), parent_size(p_size), visible_size(0) { }
~varying_part() {
buffer.cur += visible_size;
parent_size += visible_size;
}
data::in_view &buffer;
size_t &parent_size;
size_t visible_size;
constexpr size_t size() const { return visible_size; }
void* data() { return buffer.writeTo(0); }
void reserve(size_t target) {
if (target >= buffer.siz - buffer.cur) {
const auto size = target + buffer.cur;
buffer.ptr = (uint8_t *)realloc(buffer.ptr, size);
buffer.siz = size;
}
}
void resize(size_t target) {
reserve(target);
visible_size = target;
}
};
/// Only from resize, write, resize down
varying_part varying() {
return varying_part(buffer, visible_size);
}
bool isFull() const {
return buffer.cur >= visible_size;
}
[[nodiscard]]
data::out_buffer finish() {
assert(isFull());
data::out_buffer cpy(buffer.ptr, visible_size);
buffer.ptr = nullptr;
buffer.siz = 0;
return cpy;
return data::out_buffer(*this);
}
[[nodiscard]]
@ -126,50 +45,12 @@ public:
static data::out_buffer Of(net::server_packet_type type, const D& data) {
return Of(type, &data, sizeof(data));
}
private:
data::in_view buffer;
size_t visible_size;
};
/// Helper to read out_view
class PacketReader final {
class PacketReader final: public data::out_reader {
public:
PacketReader(const data::out_view& buf): buffer(buf) { }
template<typename D>
const D* read() {
return buffer.cur + sizeof(D) <= buffer.size() ?
(const D*)buffer.readFrom(sizeof(D)) : nullptr;
}
template<typename D>
bool read(D& out) {
const auto ptr = read<D>();
if (ptr == nullptr)
return false;
out = *ptr;
return true;
}
bool skip(size_t size) {
buffer.readFrom(size);
return !isFull();
}
data::out_view readPart(size_t size) {
return data::out_view(buffer.readFrom(size), size);
}
data::out_view readAll() {
return readPart(buffer.size() - buffer.cur);
}
bool isFull() const {
return buffer.isDone();
}
private:
data::out_view buffer;
PacketReader(const data::out_view& buf): data::out_reader(buf) { }
};
}

View File

@ -30,6 +30,8 @@ namespace world::server {
std::optional<double> getCurvature() const override { return GetCurvature(generator_properties); }
static std::optional<double> GetCurvature(const generator::params &);
inline const glm::vec3 getGravity(const voxel_pos& p) const { return generator->getGravity(p); }
private:
shared_guarded<regions_t> regions;

View File

@ -306,7 +306,7 @@ void Universe::update(float deltaTime) {
const auto areaDiff = glm::divide(it->second->getOffset().as_voxel());
std::vector<chunk_pos> inAreaPlayers;
players.iter([&](entity_id, Entity::Instance player) {
players.iter([&](const entity_id&, const Entity::Instance& player) {
const chunk_pos diff = glm::divide(player.pos.as_voxel() - it->second->getOffset().as_voxel());
if (glm::length2(diff) <= areaRange)
inAreaPlayers.push_back(diff);
@ -414,6 +414,9 @@ void Universe::update(float deltaTime) {
ZoneScopedN("Entities");
size_t item_count = 0;
entities.remove([&](entity_id type, Entity &val) {
//TODO: find mass center
const auto OFFSET = glm::vec3(val.size) * val.scale / 2.f;
const auto DIST2 = glm::pow2(loadDistance); //NOTE: must be < keepDistance + glm::length(OFFSET) to avoid collision miss
val.instances.remove([&](entity_id, Entity::Instance &inst) {
if (type == PLAYER_ENTITY_ID) {
//MAYBE: update players ?
@ -421,14 +424,25 @@ void Universe::update(float deltaTime) {
return false;
}
inst.pos += inst.velocity * deltaTime;
if (true /*FIXME: remove far entities ? glm::length2(glm::divide(pos - inst.pos.as_voxel())) <= glm::pow2(keepDistance);*/) {
//TODO: partition space
auto pos = inst.pos + OFFSET;
for(const auto& area: areas) {
//NOTE: consider entity mass negligible
inst.velocity += area.second->getGravity((pos - area.second->getOffset()).as_voxel()) * deltaTime;
}
//MAYBE: friction
if (move(pos, inst.velocity, 1, glm::max_axis(OFFSET))) {
inst.velocity = glm::vec3(0);
}
inst.pos = pos - OFFSET;
if (entities.at(PLAYER_ENTITY_ID).instances.contains([&](const entity_id&, const Entity::Instance& player) {
return glm::length2(glm::divide((player.pos - inst.pos).as_voxel())) <= DIST2;
})) {
item_count++;
return false;
}
//TODO: Save to files if !temporary
return true;
//MAYBE: Store in region ?
//MAYBE: Save to files
});
return !val.permanant && val.instances.empty();
});
@ -558,7 +572,7 @@ bool Universe::onPacket(net::server::Peer *peer, const data::out_view &buf, net:
break;
}
case client_packet_type::MESSAGE: {
const auto ref = packet.readAll();
const auto ref = packet.readRemaining();
broadcastMessage("Player" + std::to_string(peer->getCtx<net_client>()->instanceId.index)
+ ": " + std::string((const char*)ref.data(), ref.size()));
break;
@ -573,7 +587,7 @@ bool Universe::onPacket(net::server::Peer *peer, const data::out_view &buf, net:
break;
if (auto area = areas.find(id); area != areas.end()) {
const chunk_pos areaOffset = glm::divide(pos - area->second->getOffset().as_voxel());
while (!packet.isFull()) {
while (!packet.isDone()) {
region_pos rpos;
if (!packet.read(rpos))
break;
@ -582,10 +596,8 @@ bool Universe::onPacket(net::server::Peer *peer, const data::out_view &buf, net:
if (glm::length2(areaOffset - glm::lvec3(it_r->first) * glm::lvec3(REGION_LENGTH)) <= glm::pow2(loadDistance + REGION_LENGTH * 2)) {
auto packet = PacketWriter(server_packet_type::REGION, sizeof(area_<region_pos>));
packet.write<area_<region_pos>>(std::make_pair(id, rpos));
{
auto vec = packet.varying();
it_r->second->averages(vec);
}
packet.pushPart([&](PacketWriter::part part) {
it_r->second->averages(part); });
peer->send(packet.finish(), net::server::CHUNK);
} else {
LOG_T("Request out of range region");
@ -611,7 +623,7 @@ bool Universe::onPacket(net::server::Peer *peer, const data::out_view &buf, net:
auto &chunks = area->second->getChunks();
const chunk_pos areaOffset = glm::divide(pos - area->second->getOffset().as_voxel());
for (size_t i = 0; !packet.isFull() && i < MAX_PENDING_CHUNK_COUNT; i++) {
for (size_t i = 0; !packet.isDone() && i < MAX_PENDING_CHUNK_COUNT; i++) {
chunk_pos cpos;
if (!packet.read(cpos))
break;
@ -642,15 +654,8 @@ bool Universe::onPacket(net::server::Peer *peer, const data::out_view &buf, net:
for (auto it = area->begin(); it != area->end(); ++it) {
std::ostringstream out;
std::dynamic_pointer_cast<Chunk>(*it)->write(out);
auto size_pos = packet.getCursor();
size_t size = 0;
packet.writePush(size);
{
auto vec = packet.varying();
dict_write_ctx.compress(out.str(), vec);
size = vec.size();
}
packet.writeAt(size_pos, size);
packet.pushSizedPart([&](PacketWriter::part part) {
dict_write_ctx.compress(out.str(), part); });
}
peer->send(packet.finish(), net::server::CHUNK);
} else {
@ -691,10 +696,8 @@ data::out_buffer Universe::serializeChunk(area_<chunk_pos> id, const std::shared
data->write(out);
auto packet = net::PacketWriter(net::server_packet_type::CHUNK, sizeof(id));
packet.write(id);
{
auto vec = packet.varying();
dict_write_ctx.compress(out.str(), vec);
}
packet.pushPart([&](net::PacketWriter::part part){
dict_write_ctx.compress(out.str(), part); });
return packet.finish();
}
void Universe::broadcastMessage(const std::string& text) {

View File

@ -13,6 +13,8 @@ namespace world::generator {
/// Generate chunk voxels
/// MAYBE: use template to avoid virtual
virtual void generate(const chunk_pos &at, std::array<Voxel, CHUNK_SIZE> &out) = 0;
/// Get gravity vector at given point
virtual glm::vec3 getGravity(const voxel_pos& point) const = 0;
};
// Generate empty space
@ -24,6 +26,7 @@ namespace world::generator {
void generate(const chunk_pos &, std::array<Voxel, CHUNK_SIZE> &out) override {
out.fill(Voxel());
}
glm::vec3 getGravity(const voxel_pos&) const override { return glm::vec3(0); }
};
/// Endless cave network
@ -51,6 +54,7 @@ namespace world::generator {
out[i] = Voxel(material, density);
}
}
glm::vec3 getGravity(const voxel_pos&) const override { return glm::vec3(-1, 0, 0); }
private:
Params params;
Noise density;
@ -110,6 +114,12 @@ namespace world::generator {
}();
}
}
glm::vec3 getGravity(const voxel_pos& pos) const override {
const auto heightRatio = static_cast<float>((getHeight(pos) - params.height) / params.height);
const auto scale = params.height >> 7;
const auto mass = scale * scale * scale; //FIXME: average material density
return -getSurfaceDir(pos) * std::max(0.f, 1.f - heightRatio * heightRatio) * glm::vec3(mass);
}
private:
static constexpr PlanetShape shape = PS;
static constexpr glm::f64 getHeight(const voxel_pos &p) {
@ -119,6 +129,15 @@ namespace world::generator {
return glm::length(glm::dvec3(p));
}
}
/// Centre inverse direction
// NOTE: So center point is consider mass center
static constexpr glm::vec3 getSurfaceDir(const voxel_pos &p) {
if constexpr(shape == PlanetShape::Cube) {
return glm::inormalize(p);
} else {
return glm::normalize(glm::dvec3(p));
}
}
Params params;
Noise density;
Noise displacement;

BIN
univerxel.icns Normal file

Binary file not shown.

BIN
univerxel.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

BIN
univerxel.png (Stored with Git LFS) Normal file

Binary file not shown.

28
univerxel.rc Normal file
View File

@ -0,0 +1,28 @@
UNIVERXEL ICON "univerxel.ico"
VS_VERSION_INFO VERSIONINFO
FILEVERSION 0,0,1,0
PRODUCTVERSION 0,0,1,0
EGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904B0" /* LANG_ENGLISH/SUBLANG_ENGLISH_US, Unicode CP */
BEGIN
VALUE "CompanyName", "Shu"
VALUE "FileVersion", "0,0,1,0"
VALUE "LegalCopyright", "MIT"
VALUE "ProductName", "Univerxel"
VALUE "ProductVersion", "0,0,1,0"
VER_SPECIAL_BUILD_LINE
VER_PRIVATE_BUILD_LINE
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x0409, 0x04B0
END
END