7 Commits

Author SHA1 Message Date
zhenyan121
def311c7dd feature: hud (#37)
* build: replace FetchContent with direct includes for glm

* build(deps): replace SOIL2 with stb for image and vorbis decoding

* build: replace FetchContent with direct includes for toml++

* refactor(texture): bump block texture size to 512 and disable items tab

* feat: add hotbar system with ItemStack and RowLayout

* fix(ui): restrict Ctrl key handling to typing mode

* feat(ui): add widget border support and highlight selected hotbar slot

* fix(ui): update borders on scale change and correct Image width

- Added update_border() calls in set_scale() for Button, ChatBox, Image, Label, and TextField.
- Fixed Image::width() to multiply by width instead of height.
- Added early return in Widget::update_border() if border is not supported.

* feat(ui): add border visual feedback on slider drag

* feat(ui): add ItemSlot widget and refactor hotbar to use it

* feat(world_scene): add inventory UI and pause type enum

* feat(ui): add item slot tooltip and make window size static

* fix(ui label): account for background offset in width/height

* refactor(ui): centralize item info label in inventory and add update

* feat(ui): add hotbar interaction in inventory UI

* fix(inventory-ui): correct row creation logic for first item

The loop starting at index 0 created a new row on the first iteration
(i % 10 == 0). Shift the loop to start at 1 and adjust modulus condition
to create rows correctly every 10 items.

* feat(block): add name_key for localized block names

Add `name_key` field to all block TOML definitions, enabling per-block localization. Update `BlockData` struct and `BlockManager` with `local_name()` method that returns a localized string via the translation system. Add corresponding entries to `en_US.json` and `zh_CN.json`. Modify inventory UI to use the localized name when displaying block info, and fix a bug where emptying a hotbar slot did not clear the item locally.

* fix(ui): change inventory item slot color from white to gray

* fix(ui): add missing include for std::array
2026-07-23 14:07:11 +08:00
zhenyan121
45d2c8234a feature: chat (#36)
* fix(config): load config on demand in get/set instead of asserting

* feat(chat): implement in-game chat box with UTF-8 support

- Add ChatMessage struct and ChatBox widget for displaying and sending messages
- Introduce split_utf8() in text_tools for splitting UTF-8 strings by character count
- Add TimeTools utility (get_time_ticks) for message timestamps
- Update TextField with start_typing/stop_typing methods and set_typing()
- Integrate text input handling in WorldScene and WorldUIManager for chat
- Add utf8cpp library for robust UTF-8 validation and processing
- Add ChatBox rendering and input logic (src/ui/chat_box.cpp)
- Register ChatBox source in CMakeLists.txt
- Fix Renderer::render_lable to skip rendering if VAO is null
- Update credits to include utf8cpp library

* fix(ui): correct chat box layout with dynamic height and bottom anchor

* feat(chat): implement client-server chat messaging

Add protobuf definition for ChatMsg, new packet ID CHAT_MSG (4001), and
handle sending/receiving chat messages on both client and server side.
Wire up UI components (ChatBox, TextField) to allow message input and
display, including a message queue in ClientWorld for thread-safe
processing. Also add default initialization for ChatMessage::time.

* feat(chat): distinguish between send and cancel in chat input

* feat(ui): add background to Label widget and refactor debug overlay

* refactor(ui): decouple ChatBox from image dependencies, allow custom TextField backgrounds

TextField's background is now a generic `Widget` pointer, enabling use of any widget (e.g., `Rect`, `Image`) as background. `ChatBox` no longer creates its own `TextField`; it accepts one via `set_text_field`. Removed `set_d_image` and `set_default_image` in favor of `set_background`. Added a shared default image constant in `default_image.hpp`. Updated `host_game_ui.cpp`, `join_game_ui.cpp`, and `world_ui_manager.cpp` to construct backgrounds accordingly.

* refactor(ui): move width, height, and fill properties to base Widget

Consolidate width, height, and fill (fill_parent, fill_width, fill_height) into the base Widget class. Replace per-widget set_fill(bool) with set_fill_parent(bool), set_fill_width(bool), and set_fill_height(bool).

* refactor(ui): generalize Label background, add ChatBox width method

* feat(chat): implement message expiration after 5 seconds

* feat(ui): add word wrap for chat messages with font width measurement

* feat(audio): implement voice chat using Opus codec

Add voice chat support with Opus encoding/decoding, push-to-talk (V key), and new audio recording infrastructure.

* feat(audio): implement voice chat with Opus codec and streaming audio source

Add AudioStreamSource for continuous PCM playback. Refactor voice send/receive to encode with Opus and stream to a dedicated source instead of per-frame sources. Enable voice transmission over network.

* feat(audio): add spatial voice source with underwater effects

Update voice source to support spatial positioning, low-pass filter, and reverb effect for underwater immersion. Adjust source pool size and low-pass filter parameter. Correct receive_voice parameter type.

* feat(settings): add player voice volume and voice input type options

- Add player voice volume slider and voice input type combo in settings UI.
- Add localization strings for player voice and voice input types.
- Add config overloads for const char* default values.
- Implement voice input types (PTT, always, off) in world scene.
- Refactor WorldScene member order and add load_config method.
- Update audio engine to use player voice volume.
- Downgrade some logging to debug.

* fix(event): delegate error UI events to each handler

Move error UI event check from top of handle_event into each individual
event handler (mouse_move, mouse_button, mouse_wheel, key, text_input).
Also flatten window resize dispatch: all sub-handlers are called regardless
of previous handler return values, and the method always returns false.

* feat(chat): add colored system messages for player join/exit

* chore(logging): reduce verbosity for audio loading and input events

* fix(settings): fix typo in config key "vloume"

* feat(ui): add microphone icon when recording voice

* feat(voice): limit voice message range to 48 units

* chore(logging): downgrade text input log level to debug

* feat(credits): add license identifiers and reorganize license files

* feat: add sensitive word filter for chat messages

Implement AC automaton-based filtering using a sensitive lexicon loaded from a JSON asset file. The filter is applied to both player names and chat messages before broadcasting, and can be toggled via the `sensitive_filter` config option.

* feat(server): add config option to disable voice chat

* fix: correct utf8 validation, add missing include, remove debug log
2026-07-21 10:21:05 +08:00
zhenyan121
0f731530da refactor: logger (#35)
* refactor(log): convert Logger from namespace to class with static methods

- Replace the Logger namespace of free functions with a Logger class containing static methods (info, error, warn, debug).
- Simplify log calls by removing explicit source_location parameters; adopt `std::print` for output formatting.
- Undefine common macro names (DEBUG, INFO, ERROR, WARN) to avoid collisions.
- Update `renderer.cpp` to use `Logger::debug` instead of the old `Logger::log`.
- Add missing `#include <source_location>` in `audio_error.hpp`.
- Fix early return condition in `WorldScene::set_pause`.

* refactor(camera): remove reset_camera method and first-mouse flag

* refactor: replace --no-debug with logging options, add assertions, fix typo

- Removed `--no-debug` CLI flag and added `--logs-path`, `--log-level`, `--enable-filelog`, `--enable-consolelog`.
- Added file logging support with daily rotation, log level threshold, and console/file output control.
- Added `m_init` check in `Config::get`/`set` to prevent use before initialization.
- Fixed typo in `DebugCollector::destroy` (`distory` → `destory`).
- Removed `debug_on` parameter from `Renderer::init`; OpenGL debug output is now unconditionally enabled in debug builds.

* feat(debug): enable GL debug context flag in debug builds
2026-07-18 10:40:34 +08:00
zhenyan121
2b75c510af refactor: window (#34)
* build: add SDL3 dependency

* refactor(window): migrate from GLFW to SDL3 for window and input

* refactor: remove unused GLFW includes and commented-out code

* refactor(input): use relative mouse motion and simplify camera mouse update

* refactor(event-handling): guard input events with imgui state

Move mouse enable/disable logic into set_imgui_enabled and check imgui_enable before forwarding non-ImGui events to handlers. This prevents duplicate processing and ensures proper mouse state when toggling ImGui.

* fix(camera): reset camera on resize events and fix input handling on imgui toggle

- Add Camera::player() accessor.
- Handle WindowResizeEvent and FrameBufferResizeEvent to reset camera.
- Fix escape key handling to disable mouse before toggling imgui.
- Fix left alt handling to toggle mouse state correctly.
- Reset player key status when toggling imgui.

* fix(client_world): add direct exit flag to break connection error loop

* refactor(debug): add d_rep template and remove widget ID

* feat(debug_collector): add video driver display to debug UI

* fix: invert debug flag logic and rename to no_debug

The previous 'debug_on' field was never set to true by default, causing
debug mode to never be enabled. Now renamed to 'no_debug' and the
renderer initializes with debug enabled unless the --no-debug flag is
given.

* feat(window): add video driver selection via config and --video-driver argument

* ci: add vcpkg cache

* fix: mscv cpp version show

* fix: context delete too early

* fix: ci build fail

* fix: opengl resource delete error

* fix: add // NOLINT for NvOptimusEnablement and AmdPowerXpressRequestHighPerformance

* fix: windows ime fail on fullscreen

* feat(ui): update text input area on field changes and window resize

* fix(ui): initialize m_app pointer to nullptr

* feat(window): add exclusive fullscreen support with WM detection

* refactor: simplify WM detection and exclusive fullscreen default

Removed WindowManager enum; detect_wm() now returns string directly.
Default exclusive fullscreen based on platform (true on Linux).
Added WM info to debug overlay.

* feat: add Alt key to close ImGui; clarify debug log

* refactor(dev-panel): remove direct settings UI from settings tab
2026-07-17 18:08:51 +08:00
zhenyan121
d6e1e4a50e refactor: argument (#33)
* refactor(config): remove TOML config loading and replace is_client with host_game

* feat(network): add net_error_message helper and use for error logging
2026-07-17 09:00:26 +08:00
zhenyan121
d62b310a21 feature: localization (#32)
* build: add nlohmann/json library

* build(deps): add harfbuzz dependency

* feat(cmake): add guard to reject macOS builds

* feat(font): add HarfBuzz shaping and replace font with unifont

* feat(localization): add i18n support with en_US and zh_CN translations

Implement Localization class to load JSON translation files. Replace hardcoded strings in UI with translation keys and update sliders/combo buttons to use dynamic text.

* feat(localization): detect system locale for language default

* feat(settings): add language selection option with restart notification

* fix: use vckpg to add freetype on windows
2026-07-16 17:11:53 +08:00
zhenyan121
cc2df5e4ec feature: UI (#31)
* refactor(ui): replace Text with Label/Widget component system

Replace the old `Text` class with a new component-based UI system consisting of `Widget` and `Label` classes. Key changes:

- Remove `Text` class and its GPU upload logic; introduce `Label` as a `Widget` subclass with dirty-flag vertex update.
- Add `UIVertexData` struct to manage per-label vertex buffer/array and memory.
- Refactor `DebugCollector` to store labels in a `Widget` tree, removing hash-based lookup.
- Simplify `Font::vertices()` signature by removing position/scale parameters; scaling is now applied in the renderer’s model matrix.
- Update `Renderer` with `render_label()` and move UI rendering to a `render_ui()` that renders the widget tree.
- Add new source files `label.cpp`, `widget.cpp`, `ui_vertex_data.cpp` and update CMakeLists.

* refactor(render): replace UI shaders with image shaders and Image widget

* refactor(core): add event system and scene management

- Introduce Event variant and Overloaded helper for input handling
- Move game state (camera, client_world, dev_panel) from App to WorldScene
- Add SceneManager with push/pop/change operations for scene stack
- Refactor input callbacks to dispatch events through scene hierarchy
- Extract Argument struct to separate header
- Update Renderer and WorldRenderer to accept world reference
- Replace global input state with event-driven processing in ClientPlayer, Camera, etc.

* refactor: add framebuffer resize event and separate UI management

* fix(font): use correct texture format and clamp to edge for font atlas

* feat(input): add KeyAction::REPEAT for key repeat events

Add a new REPEAT value to the KeyAction enum and update the input callbacks to map GLFW_REPEAT actions to the new value instead of treating them as press.

* feat(client-player): add block cooldown and track mouse state

Introduce a time-based cooldown for block placement (PLACE_BLOCK_INTERVAL) to prevent rapid clicking. Replace the global InputState with per-player MouseState tracking in ClientPlayer. Rename place_block() to get_current_block() for clarity. Remove the old src/input.cpp and the Cubed/input.hpp header, moving input structures to Cubed/input/input.hpp.

* ui: add main menu scene and interactive button widget

- Implement MainMenuScene with a "Start Game" button that pushes the WorldScene.
- Extend Button class with background/foreground, hover detection, and size queries.
- Add virtual width/height and event handlers to Widget base class.
- Update WorldUIManager to propagate mouse move events.
- Adjust Window mouse mode based on game running state.

* feat(ui): add button scaling and change square vertices to top-left origin

* refactor(font): return TextMesh from Font::vertices to include bounding box

Replace the plain vertex vector with a TextMesh struct that carries width, height, and min coordinates. Update Label to compute and store these values immediately on text change (removing lazy dirty update). Fix Button's width/height to factor in scale and use concrete widget types for background and foreground.

* feat(ui): support scaling of labels and images

Modify width() and height() methods to multiply by m_scale for both Label and Image. Rename internal dimensions to m_real_width and m_real_height for clarity. Adjust Button foreground position to vertically center when label is scaled.

* refactor(ui): move scale property to subclasses and fix resize event handling

Remove scale from base Widget class and add per-type set_scale/scale methods to Button, Image, and Label. Dispatch separate WindowResizeEvent alongside existing FrameBufferResizeEvent. Correct centering calculations from `+` to `-` in main menu and world UI managers.

* fix(render): remove duplicate scaling in render_image model matrix

* feat(ui): introduce anchor-based widget positioning system

Replace set_position with anchor and offset for relative positioning. Add parent pointer to widget hierarchy. Remove manual resize logic in UI managers.

* feat(ui): introduce ColumnLayout widget and refactor widget parenting

- Add ColumnLayout widget that arranges children vertically with spacing.
- Refactor Widget::add_child to automatically pass `this` as parent.
- Update DebugCollector to use ColumnLayout for consistent spacing.
- Expose children() accessor in Widget for layout management.

* feat(main_menu): add exit button and refactor layout with ColumnLayout

- Add `should_close_window()` method to force window close from UI.
- Replace standalone button with ColumnLayout for proper centering.
- Add "Exit" button that calls `should_close_window()`.
- Update ESC key handling to only close window when game is not running.
- Fix ColumnLayout to respect parent's anchor instead of hardcoded TOP_LEFT.
- Remove unused parameter name in Image::update for consistency.

* feat(ui): add Rect widget with fill, color, and parent-relative sizing

* refactor(ui): extract common UI manager logic into base class

* refactor: move server logic to WorldScene and refactor UI methods

* feat(scene): implement pause menu with UI manager and escape key toggling

Add PauseMenuUIManager class and integrate pause state into WorldScene.
Escape key toggles pause in ClientPlayer and PauseMenuUIManager.
Refactor Window mouse and imgui control to support pause state.

* refactor(scene): dispatch events via typed handlers and move pause toggle to WorldScene

* feat(ui): add HUD visibility toggle and image fill features

* refactor(app): remove redundant ImGui WantCaptureMouse check in cursor callback

* feat(scene): add credits scene and UI

* feat(credits): add ESC key handling; remove about tab from dev panel

* feat(ui): add Slider widget for value selection

* refactor(ui/slider): improve track/thumb initialization and validation

Rename track/thumb accessors from set_track/set_thumb to get_track/get_thumb.
Add private init methods and a constant THUMB_WIDTH.
Add validation to set_width with log and assert.
Modify set_height to assume non-null m_thumb.

* refactor(ui): simplify Button API with property setters

* feat(ui): add label and image support to slider

- Add `set_text()`, `set_track_image()`, `set_thumb_image()` methods.
- Include label rendering and auto-scaling based on slider dimensions.
- Replace static `THUMB_WIDTH` with dynamic thumb sizing.
- Add slider thumb and track texture assets.

* feat(ui): add int slider support and fix update_text_scale calls

* refactor(config): rename hot_reload to reload_config and add settings scene

* feat(ui): add auto-scaling option for button and slider text

* feat(ui): add button enable/disable and reset on re-enter

Prevent double-clicking on buttons that push new scenes by disabling them on click and re-enabling when the UI manager's on_re_enter is called.

* fix(input): reset key status on pause

* feat(ui): add ComboButton widget and integrate into settings

Introduce ComboButton, a button that cycles through options on click.
Used in SettingsUI for fullscreen, V-Sync, and anisotropic filtering.
Adds texture reload support in SettingsScene and makes Button methods
virtual to allow overriding. Also fixes default mouse_enable to true.

* feat(ui): add child anchor and content sizing to column layout

* feat(ui): add TextField widget with UTF-8 text input support

* feat(scene): add host game scene and world parameter handling

* feat(scene): add join game scene and UI

* feat(ui): add clipboard paste to TextField

* feat(ui): add blinking cursor to text field

* feat(ui): add error UI for connection failures and network errors

Introduce ErrorUI to display error messages in-world when the client encounters connection failures or network errors. The network client now stores error strings thread-safely and exposes them via `get_error_string()`. The world scene suspends normal update/render when an error is active and shows the error overlay, allowing the user to return to the previous scene. Additionally, the client world checks for connection errors during exit to avoid hanging, and the join game UI is fixed to correctly default to client mode.

* refactor: move connect error check to WorldScene and add UI titles

* feat(ui): add error display in host and join game screens

* feat(ui): add configurable child traversal order for event handling

Add TraversalOrder enum to Widget, allowing event handlers to iterate children in back-to-front (default) or front-to-back order.
Set ColumnLayout to front-to-back order.
Change TextField mouse button handler to return false, enabling event propagation.

* fix: window build fail

* style(ui): remove extra blank line
2026-07-16 11:30:27 +08:00
704 changed files with 135866 additions and 2728 deletions

View File

@@ -27,7 +27,8 @@ jobs:
runs-on: ${{ matrix.os }}
env:
VCPKG_BINARY_SOURCES: clear;files,${{ github.workspace }}/.vcpkg-cache,readwrite
steps:
- uses: actions/checkout@v4
@@ -48,12 +49,18 @@ jobs:
- name: Setup MSVC
if: matrix.platform == 'windows'
uses: ilammy/msvc-dev-cmd@v1
- name: Cache vcpkg
uses: actions/cache@v4
with:
path: .vcpkg-cache
key: ${{ runner.os }}-vcpkg-${{ hashFiles('vcpkg.json') }}
restore-keys: |
${{ runner.os }}-vcpkg-
- name: Bootstrap vcpkg
run: |
git clone https://github.com/microsoft/vcpkg.git
./vcpkg/bootstrap-vcpkg.bat
- name: Configure
if: matrix.platform == 'windows'
run: >

View File

@@ -1,4 +1,9 @@
cmake_minimum_required(VERSION 3.21)
if(APPLE)
message(FATAL_ERROR "This game does not support macOS")
endif()
project(Cubed LANGUAGES C CXX)
set(CMAKE_CXX_STANDARD 23)
@@ -34,6 +39,8 @@ add_subdirectory(third_party/imgui)
add_subdirectory(third_party/dr_libs)
add_subdirectory(third_party/stb)
add_executable(${PROJECT_NAME})
add_subdirectory(src)
@@ -123,16 +130,14 @@ target_include_directories(${PROJECT_NAME}
target_link_libraries(${PROJECT_NAME}
PRIVATE
glfw
SDL3::SDL3
glad
glm::glm
OpenGL::GL
soil2
Freetype::Freetype
tomlplusplus::tomlplusplus
imgui
dr_libs
tbb
stb
protobuf::libprotobuf
absl::log
absl::check
@@ -141,6 +146,8 @@ target_link_libraries(${PROJECT_NAME}
absl::flat_hash_map
zstd::zstd
OpenAL::OpenAL
harfbuzz::harfbuzz
Opus::Opus
$<$<PLATFORM_ID:Windows>:ws2_32>
)

File diff suppressed because one or more lines are too long

View File

@@ -8,4 +8,5 @@ is_passable = true
is_transitional = false
is_transparent = true
name = 'air'
name_key = 'block.air'
roughness = 1.0

View File

@@ -8,4 +8,5 @@ is_passable = false
is_transitional = true
is_transparent = false
name = 'dirt'
name_key = 'block.dirt'
roughness = 1.0

View File

@@ -8,4 +8,5 @@ is_passable = true
is_transitional = false
is_transparent = true
name = 'grass'
roughness = 0.9
name_key = 'block.grass'
roughness = 0.90000000000000002

View File

@@ -8,4 +8,5 @@ is_passable = false
is_transitional = true
is_transparent = false
name = 'grass_block'
roughness = 0.9
name_key = 'block.grass_block'
roughness = 0.90000000000000002

View File

@@ -8,4 +8,5 @@ is_passable = false
is_transitional = false
is_transparent = true
name = 'leaf'
roughness = 0.7
name_key = 'block.leaf'
roughness = 0.69999999999999996

View File

@@ -8,4 +8,5 @@ is_passable = false
is_transitional = false
is_transparent = false
name = 'log'
roughness = 0.7
name_key = 'block.log'
roughness = 0.69999999999999996

View File

@@ -8,4 +8,5 @@ is_passable = false
is_transitional = true
is_transparent = false
name = 'sand'
roughness = 0.8
name_key = 'block.sand'
roughness = 0.80000000000000004

View File

@@ -8,4 +8,5 @@ is_passable = false
is_transitional = true
is_transparent = false
name = 'snowy_grass_block'
roughness = 0.9
name_key = 'block.snowy_grass_block'
roughness = 0.90000000000000002

View File

@@ -8,4 +8,5 @@ is_passable = false
is_transitional = true
is_transparent = false
name = 'stone'
name_key = 'block.stone'
roughness = 0.75

View File

@@ -8,4 +8,5 @@ is_transparent = false
is_discard = false
is_blend = false
is_transitional = false
roughness = 1.0
roughness = 1.0
name_key = "block.template"

View File

@@ -8,4 +8,5 @@ is_passable = true
is_transitional = false
is_transparent = true
name = 'water'
name_key = 'block.water'
roughness = 0.02

Binary file not shown.

Binary file not shown.

49
assets/lang/en_US.json Normal file
View File

@@ -0,0 +1,49 @@
{
"menu.settings": "Settings",
"menu.main.host_game": "Host Game",
"menu.main.join_game": "Join Game",
"menu.main.credits": "Credits",
"menu.main.quit": "Quit",
"menu.main.player_name": "Player: {name}",
"menu.main.cubed_version": "Cubed: {version}",
"menu.pause.pause_menu": "Pause Menu",
"menu.pause.back_to_game": "Back To Game",
"menu.pause.return_to_main_menu": "Return to Menu",
"button.done": "Done",
"button.return": "Return",
"settings.title": "Settings",
"settings.fov": "Fov: {fov}",
"settings.mouse_sensitivity": "Sensitivity: {sensitivity}",
"settings.distance": "Distance: {distance}",
"settings.music_volume": "Music: {music}",
"settings.sfx_volume": "SFX: {sfx}",
"settings.player_voice": "Player Voice: {volume}",
"settings.fullscreen": "fullscreen: {state}",
"settings.aniso": "Aniso: {aniso}",
"settings.vsync": "V-Sync: {state}",
"settings.language": "Language: {lang}",
"settings.restart_game_info": "You need to restart the game to apply this changes.",
"settings.voice_input": "Voice Input: {type}",
"voice_input.ptt": "PTT",
"voice_input.always": "always",
"common.on": "on",
"common.off": "off",
"hostgame.create_a_new_world": "Create A New World",
"hostgame.world_seed": "World Seed",
"hostgame.port": "Port",
"hostgame.create_world": "Create World",
"joingame.join_a_world": "Join A World",
"joingame.server_ip": "Server Ip",
"joingame.join_world": "Join World",
"error.disable_voice": "Server Disable Voice Chat",
"block.air": "air",
"block.dirt": "dirt",
"block.grass_block": "grass block",
"block.grass": "grass",
"block.leaf": "leaf",
"block.log": "log",
"block.sand": "sand",
"block.snowy_grass_block": "snowy grass block",
"block.stone": "stone",
"block.water": "water"
}

49
assets/lang/zh_CN.json Normal file
View File

@@ -0,0 +1,49 @@
{
"menu.settings": "设置",
"menu.main.host_game": "创建游戏",
"menu.main.join_game": "加入游戏",
"menu.main.credits": "制作人员",
"menu.main.quit": "退出",
"menu.main.player_name": "玩家:{name}",
"menu.main.cubed_version": "Cubed{version}",
"menu.pause.pause_menu": "暂停菜单",
"menu.pause.back_to_game": "返回游戏",
"menu.pause.return_to_main_menu": "返回主菜单",
"button.done": "完成",
"button.return": "返回",
"settings.title": "设置",
"settings.fov": "视野:{fov}",
"settings.mouse_sensitivity": "灵敏度:{sensitivity}",
"settings.distance": "距离:{distance}",
"settings.music_volume": "音乐:{music}",
"settings.sfx_volume": "音效:{sfx}",
"settings.player_voice": "玩家语音:{volume}",
"settings.fullscreen": "全屏:{state}",
"settings.aniso": "各向异性过滤:{aniso}",
"settings.vsync": "垂直同步: {state}",
"settings.language": "语言: {lang}",
"settings.restart_game_info": "您需要重启游戏以应用这些更改。",
"settings.voice_input": "语音输入: {type}",
"voice_input.ptt": "按键通话",
"voice_input.always": "始终",
"common.on": "开",
"common.off": "关",
"hostgame.create_a_new_world": "创建新世界",
"hostgame.world_seed": "世界种子",
"hostgame.port": "端口",
"hostgame.create_world": "创建世界",
"joingame.join_a_world": "加入世界",
"joingame.server_ip": "服务器IP",
"joingame.join_world": "加入世界",
"error.disable_voice": "服务器已禁用语音聊天",
"block.air": "空气",
"block.dirt": "泥土",
"block.grass_block": "草方块",
"block.grass": "草",
"block.leaf": "树叶",
"block.log": "原木",
"block.sand": "沙子",
"block.snowy_grass_block": "覆雪草方块",
"block.stone": "石头",
"block.water": "水"
}

View File

@@ -0,0 +1,90 @@
The SIL Open Font License version 1.1 is copied below, and is also
available with a FAQ at http://scripts.sil.org/OFL.
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

View File

@@ -0,0 +1,437 @@
GNU LIBRARY GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1991 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the library GPL. It is
numbered 2 because it goes with version 2 of the ordinary GPL.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Library General Public License, applies to some
specially designated Free Software Foundation software, and to any
other libraries whose authors decide to use it. You can use it for
your libraries, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if
you distribute copies of the library, or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link a program with the library, you must provide
complete object files to the recipients so that they can relink them
with the library, after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
Our method of protecting your rights has two steps: (1) copyright
the library, and (2) offer you this license which gives you legal
permission to copy, distribute and/or modify the library.
Also, for each distributor's protection, we want to make certain
that everyone understands that there is no warranty for this free
library. If the library is modified by someone else and passed on, we
want its recipients to know that what they have is not the original
version, so that any problems introduced by others will not reflect on
the original authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that companies distributing free
software will individually obtain patent licenses, thus in effect
transforming the program into proprietary software. To prevent this,
we have made it clear that any patent must be licensed for everyone's
free use or not licensed at all.
Most GNU software, including some libraries, is covered by the ordinary
GNU General Public License, which was designed for utility programs. This
license, the GNU Library General Public License, applies to certain
designated libraries. This license is quite different from the ordinary
one; be sure to read it in full, and don't assume that anything in it is
the same as in the ordinary license.
The reason we have a separate public license for some libraries is that
they blur the distinction we usually make between modifying or adding to a
program and simply using it. Linking a program with a library, without
changing the library, is in some sense simply using the library, and is
analogous to running a utility program or application program. However, in
a textual and legal sense, the linked executable is a combined work, a
derivative of the original library, and the ordinary General Public License
treats it as such.
Because of this blurred distinction, using the ordinary General
Public License for libraries did not effectively promote software
sharing, because most developers did not use the libraries. We
concluded that weaker conditions might promote sharing better.
However, unrestricted linking of non-free programs would deprive the
users of those programs of all benefit from the free status of the
libraries themselves. This Library General Public License is intended to
permit developers of non-free programs to use free libraries, while
preserving your freedom as a user of such programs to change the free
libraries that are incorporated in them. (We have not seen how to achieve
this as regards changes in header files, but we have achieved it as regards
changes in the actual functions of the Library.) The hope is that this
will lead to faster development of free libraries.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, while the latter only
works together with the library.
Note that it is possible for a library to be covered by the ordinary
General Public License rather than by this special one.
GNU LIBRARY GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library which
contains a notice placed by the copyright holder or other authorized
party saying it may be distributed under the terms of this Library
General Public License (also called "this License"). Each licensee is
addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also compile or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
c) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
d) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the source code distributed need not include anything that is normally
distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if
written in the body of this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Library General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS

View File

@@ -0,0 +1,11 @@
#version 460
in vec2 tc;
out vec4 color;
layout (binding = 0) uniform sampler2D samp;
void main(void) {
color = texture(samp, tc);
}

View File

@@ -0,0 +1,14 @@
#version 460
layout (location = 0) in vec2 pos;
layout (location = 1) in vec2 texCoord;
out vec2 tc;
uniform mat4 model_matrix;
uniform mat4 proj_matrix;
void main(void) {
gl_Position = proj_matrix * model_matrix * vec4(pos, 0.0, 1.0);
tc = texCoord;
}

View File

@@ -0,0 +1,9 @@
#version 460
out vec4 color;
uniform vec4 inColor;
void main(void) {
color = inColor;
}

View File

@@ -0,0 +1,11 @@
#version 460
layout (location = 0) in vec2 pos;
uniform mat4 model_matrix;
uniform mat4 proj_matrix;
void main(void) {
gl_Position = proj_matrix * model_matrix * vec4(pos, 0.0, 1.0);
}

View File

@@ -1,12 +0,0 @@
#version 460
in vec2 tc;
flat in int tex_layer;
out vec4 color;
layout (binding = 0) uniform sampler2DArray samp;
void main(void) {
color = texture(samp, vec3(tc, tex_layer));
}

View File

@@ -1,18 +0,0 @@
#version 460
layout (location = 0) in vec2 pos;
layout (location = 1) in vec2 texCoord;
layout (location = 2) in float layer;
out vec2 tc;
flat out int tex_layer;
uniform mat4 m_matrix;
uniform mat4 proj_matrix;
void main(void) {
gl_Position = proj_matrix * m_matrix * vec4(pos, 0.0, 1.0);
tc = texCoord;
tex_layer = int(layer);
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 99 B

After

Width:  |  Height:  |  Size: 355 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 882 B

After

Width:  |  Height:  |  Size: 231 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 277 B

After

Width:  |  Height:  |  Size: 893 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 499 B

After

Width:  |  Height:  |  Size: 239 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 890 B

After

Width:  |  Height:  |  Size: 249 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 874 B

After

Width:  |  Height:  |  Size: 223 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 877 B

After

Width:  |  Height:  |  Size: 201 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 914 B

After

Width:  |  Height:  |  Size: 215 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 915 B

After

Width:  |  Height:  |  Size: 215 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 874 B

After

Width:  |  Height:  |  Size: 215 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 215 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 174 B

BIN
assets/texture/ui/slot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 174 B

View File

@@ -6,60 +6,15 @@ find_package(Protobuf REQUIRED)
find_package(absl REQUIRED)
find_package(zstd REQUIRED)
find_package(OpenAL REQUIRED)
if (UNIX AND NOT APPLE)
find_package(Freetype REQUIRED)
find_package(glfw3 REQUIRED)
endif()
find_package(harfbuzz REQUIRED)
find_package(Freetype REQUIRED)
find_package(SDL3 REQUIRED)
find_package(Opus REQUIRED)
# Third-party libraries
FetchContent_Declare(
glm
GIT_REPOSITORY https://github.com/g-truc/glm.git
GIT_TAG 1.0.3
)
FetchContent_MakeAvailable(glm)
FetchContent_Declare(
soil2
GIT_REPOSITORY https://github.com/SpartanJ/SOIL2.git
GIT_TAG 1.31
)
FetchContent_MakeAvailable(soil2)
FetchContent_Declare(
tomlplusplus
GIT_REPOSITORY https://github.com/marzer/tomlplusplus.git
GIT_TAG v3.4.0
)
FetchContent_MakeAvailable(tomlplusplus)
if (WIN32)
FetchContent_Declare(
glfw
GIT_REPOSITORY https://github.com/glfw/glfw.git
GIT_TAG 3.4
)
set(GLFW_BUILD_DOCS OFF CACHE BOOL "" FORCE)
set(GLFW_BUILD_TESTS OFF CACHE BOOL "" FORCE)
set(GLFW_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
set(GLFW_INSTALL OFF CACHE BOOL "" FORCE)
set(GLFW_VULKAN_STATIC ON CACHE BOOL "" FORCE)
set(BUILD_SHARED_LIBS ON CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(glfw)
FetchContent_Declare(
freetype
GIT_REPOSITORY https://gitlab.freedesktop.org/freetype/freetype.git
GIT_TAG VER-2-14-3
)
FetchContent_MakeAvailable(freetype)
if(TARGET freetype)
add_library(Freetype::Freetype ALIAS freetype)
endif()
set(_BUILD_SHARED_LIBS_SAVED ${BUILD_SHARED_LIBS})
set(BUILD_SHARED_LIBS ON)

View File

@@ -0,0 +1,25 @@
include(FindPackageHandleStandardArgs)
find_path(OPUS_INCLUDE_DIR
NAMES opus/opus.h opus.h
)
find_library(OPUS_LIBRARY
NAMES opus
)
find_package_handle_standard_args(
Opus
REQUIRED_VARS
OPUS_INCLUDE_DIR
OPUS_LIBRARY
)
if(Opus_FOUND)
add_library(Opus::Opus UNKNOWN IMPORTED)
set_target_properties(Opus::Opus PROPERTIES
IMPORTED_LOCATION "${OPUS_LIBRARY}"
INTERFACE_INCLUDE_DIRECTORIES "${OPUS_INCLUDE_DIR}"
)
endif()

View File

@@ -1,84 +1,72 @@
#pragma once
#include "Cubed/argument.hpp"
#include "Cubed/audio/audio_engine.hpp"
#include "Cubed/gameplay/client_world.hpp"
#include "Cubed/gameplay/network_server.hpp"
#include "Cubed/gameplay/server_world.hpp"
#define GLFW_INCLUDE_NONE
#include "Cubed/camera.hpp"
#include "Cubed/config.hpp"
#include "Cubed/dev_panel.hpp"
#include "Cubed/render/renderer.hpp"
#include "Cubed/scene/scene_manager.hpp"
#include "Cubed/texture_manager.hpp"
#include "Cubed/window.hpp"
namespace Cubed {
class App {
public:
struct Argument {
bool is_client = false;
int port = 25530;
std::string ip{"127.0.0.1"};
std::string player{"Unknown"};
bool debug_on = true;
};
App();
~App();
static void cursor_position_callback(GLFWwindow* window, double xpos,
double ypos);
static void key_callback(GLFWwindow* window, int key, int scancode,
int action, int mods);
static void mouse_button_callback(GLFWwindow* window, int button,
int action, int mods);
static void window_focus_callback(GLFWwindow* window, int focused);
static void window_reshape_callback(GLFWwindow* window, int new_width,
int new_height);
static void mouse_scroll_callback(GLFWwindow* window, double xoffset,
double yoffset);
static void cursor_enter_callback(GLFWwindow* window, int entered);
static void char_callback(GLFWwindow* window, unsigned int ch);
void handle_mouse_move(float xpos, float ypos, float xrel, float yrel);
void handle_sdl_key(SDL_Event& e);
void handle_sdl_mouse_button(SDL_Event& e);
void handle_window_focus(bool focused);
void handle_window_resize(int new_width, int new_height);
void handle_framebuffer_resize(int new_width, int new_height);
void handle_mouse_scroll(float xoffset, float yoffset);
void handle_text_input(const char* text);
static int start_cubed_application(int argc, char** argv);
static unsigned int seed();
static float delta_time();
static float get_fps();
Camera& camera();
DevPanel& dev_panel();
Renderer& renderer();
TextureManager& texture_manager();
Window& window();
ClientWorld& client_world();
ServerWorld& server_world();
Config& config();
const Argument& argument() const;
AudioEngine& audio();
std::string get_clipboard_text();
void start_text_input();
void stop_text_input();
void update_text_input_area(const glm::vec4& textbox,
float cursor_position_x);
private:
Config m_game_config;
Camera m_camera;
TextureManager m_texture_manager;
NetworkServer m_server;
std::shared_ptr<NetworkClient> m_client;
AudioEngine m_audio;
ClientWorld m_client_world;
DevPanel m_dev_panel;
Renderer m_renderer;
Window m_window;
inline static double last_time = glfwGetTime();
inline static double current_time = glfwGetTime();
inline static double dt = 0.0f;
inline static double fps_time_count = 0.0f;
TextureManager m_texture_manager;
AudioEngine m_audio;
Renderer m_renderer;
SceneManager m_scene_manager;
inline static uint64_t last_tick = 0;
inline static uint64_t current_tick = 0;
inline static float dt = 0.0f;
inline static float fps_time_count = 0.0f;
inline static int frame_count = 0;
inline static int fps = 0;
Argument m_argument;
bool m_running = true;
bool m_opengl_init = false;
void init(int argc, char** argv);
void handle_argument(int argc, char** argv);
void handle_toml();
auto init_camera();
auto init_texture();
auto init_world();
@@ -86,6 +74,10 @@ private:
void render();
void run();
void update();
void handle_sdl_event(SDL_Event& e);
void dispatch_event(const Event& e);
};
} // namespace Cubed

View File

@@ -0,0 +1,17 @@
#pragma once
#include <optional>
#include <string>
namespace Cubed {
struct Argument {
std::optional<int> port;
std::optional<std::string> ip;
std::optional<std::string> player;
std::optional<std::string> language;
std::optional<std::string> video_driver;
std::optional<bool> enable_exclusive;
std::optional<std::string> logs_path;
std::optional<int> log_level;
std::optional<bool> enable_filelog;
std::optional<bool> enable_consolelog;
};
} // namespace Cubed

View File

@@ -1,7 +1,9 @@
#pragma once
#include "Cubed/audio/audio_fade.hpp"
#include "Cubed/audio/audio_recording.hpp"
#include "Cubed/audio/audio_source.hpp"
#include "Cubed/audio/audio_stream_source.hpp"
#include "Cubed/audio/sound_manager.hpp"
#include "Cubed/audio/source_pool.hpp"
#include "Cubed/config.hpp"
@@ -10,13 +12,15 @@
#include <AL/alc.h>
#include <glm/glm.hpp>
#include <memory>
#include <opus/opus.h>
#include <string>
#include <unordered_map>
namespace Cubed {
class ClientWorld;
class NetworkClient;
class AudioEngine {
public:
AudioEngine(Config& config);
AudioEngine(const AudioEngine&) = delete;
@@ -27,6 +31,7 @@ public:
void init();
void play_bgm();
void stop_bgm();
void change_bgm(const std::string& sound);
void play_3d(const std::string& sound, const glm::vec3& pos,
bool check = false);
@@ -40,11 +45,23 @@ public:
float& bgm_target_volume();
void set_client(std::weak_ptr<NetworkClient> client);
void send_voice(const std::array<int16_t, AudioRecording::FRAME_SAMPLES>&);
void receive_voice(std::span<const char> opus, const glm::vec3& pos);
AudioRecording& audio_recording();
const AudioRecording& audio_recording() const;
private:
using FadeMap = std::unordered_map<std::string, AudioFade>;
bool m_init{false};
ALCdevice* device{nullptr};
ALCcontext* context{nullptr};
OpusEncoder* m_encoder{nullptr};
OpusDecoder* m_decoder{nullptr};
AudioRecording m_recording;
std::weak_ptr<NetworkClient> m_client;
glm::vec3 listener_pos;
std::unique_ptr<AudioSource> m_bgm;
FadeMap m_fade_map;
@@ -55,6 +72,9 @@ private:
bool m_underwater = false;
float m_music_volume = 1.0f;
float m_sfx_volume = 1.0f;
float m_player_voice_volume = 1.0f;
std::unique_ptr<AudioStreamSource> m_voice_source;
std::unique_ptr<AudioFilter> m_low_pass_filter;
std::unique_ptr<AudioEffect> m_underwater_effect;
std::unique_ptr<AudioEffectSlot> m_underwater_slot;

View File

@@ -2,6 +2,7 @@
#include "Cubed/tools/log.hpp"
#include <AL/al.h>
#include <source_location>
namespace Cubed {
inline void check_al_error(
std::source_location loaction = std::source_location::current()) {

View File

@@ -0,0 +1,33 @@
#pragma once
#include <alc.h>
#include <array>
#include <cstdint>
namespace Cubed {
class AudioEngine;
class AudioRecording {
public:
static constexpr int SAMPLE_RATE = 48000;
static constexpr int FRAME_MS = 20;
static constexpr int FRAME_SAMPLES = SAMPLE_RATE * FRAME_MS / 1000;
AudioRecording(AudioEngine& engine);
AudioRecording(const AudioRecording&) = delete;
AudioRecording(AudioRecording&&) = delete;
AudioRecording& operator=(const AudioRecording&) = delete;
AudioRecording& operator=(AudioRecording&&) = delete;
~AudioRecording();
void update();
void init();
void start();
void stop();
bool is_recording() const;
private:
AudioEngine& m_engine;
ALCdevice* m_capture = nullptr;
bool m_recording = false;
void send_voice(const std::array<int16_t, FRAME_SAMPLES>& pcm);
};
} // namespace Cubed

View File

@@ -5,6 +5,7 @@
#include <AL/al.h>
#include <glm/glm.hpp>
#include <memory>
namespace Cubed {
enum class AudioState { INITIAL, PLAYING, PAUSED, STOPPED };
@@ -21,8 +22,9 @@ public:
void set_pitch(float pitch);
void play();
void play_2d(const AudioBuffer& buffer);
void play_2d(std::unique_ptr<AudioBuffer> buffer);
void play_3d(const AudioBuffer& buffer, const glm::vec3& pos);
void play_3d(std::unique_ptr<AudioBuffer> buffer, const glm::vec3& pos);
void stop();
void pause();
float duration() const;
@@ -42,6 +44,8 @@ public:
void clear_effect_slot();
private:
std::unique_ptr<AudioBuffer> m_buffer;
ALuint m_source = 0;
float m_target_volume = 1.0f;
float m_duration = 0.0f;

View File

@@ -0,0 +1,46 @@
#pragma once
#include "Cubed/audio/audio_effect_slot.hpp"
#include "Cubed/audio/audio_filter.hpp"
#include "glm/ext/vector_float3.hpp"
#include <al.h>
#include <array>
#include <cstdint>
#include <span>
#include <vector>
namespace Cubed {
class AudioStreamSource {
public:
static constexpr int NUM_BUFFERS = 4;
AudioStreamSource();
AudioStreamSource(const AudioStreamSource&) = delete;
AudioStreamSource(AudioStreamSource&&) = delete;
AudioStreamSource& operator=(const AudioStreamSource&) = delete;
AudioStreamSource& operator=(AudioStreamSource&&) = delete;
~AudioStreamSource();
void push_pcm(std::span<const int16_t> pcm, ALsizei sample_rate);
void stop();
bool is_playing() const;
void reset();
void set_volume(float volume);
void set_pitch(float pitch);
void set_pos(glm::vec3 pos);
void set_filter(const AudioFilter& filter);
void clear_filter();
void set_effect_slot(const AudioEffectSlot& slot);
void clear_effect_slot();
private:
ALuint m_source = 0;
std::array<ALuint, NUM_BUFFERS> m_buffers{};
std::vector<ALuint> m_free_buffers;
bool m_playing = false;
void unqueue_processed();
};
} // namespace Cubed

View File

@@ -4,7 +4,7 @@
namespace Cubed {
class SourcePool {
private:
std::vector<AudioSource> m_sources;
std::vector<std::unique_ptr<AudioSource>> m_sources;
public:
explicit SourcePool(size_t size);
@@ -15,9 +15,10 @@ public:
SourcePool& operator=(SourcePool&&) = delete;
void update();
[[nodiscard]]
AudioSource* acquire();
std::vector<AudioSource>& sources();
std::vector<std::unique_ptr<AudioSource>>& sources();
};
} // namespace Cubed

View File

@@ -1,7 +1,7 @@
#pragma once
#define GLFW_INCLUDE_NONE
#include <GLFW/glfw3.h>
#include "Cubed/input/event.hpp"
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
@@ -18,7 +18,6 @@ private:
THIRD_PERSON_FRONT,
};
bool m_firse_mouse = true;
ClientPlayer* m_player;
float m_last_mouse_x, m_last_mouse_y;
glm::vec3 m_camera_pos;
@@ -28,6 +27,9 @@ private:
glm::vec3 camera_collision(glm::vec3 start, glm::vec3 end,
float radius = 0.2f);
bool handle_key_event(const KeyEvent& e);
bool handle_mouse_move_event(const MouseMoveEvent& e);
public:
Camera();
@@ -35,8 +37,7 @@ public:
void camera_init(ClientPlayer* player);
void hot_reload();
void reset_camera();
void update_cursor_position_camera(double xpos, double ypos);
void update_cursor_position_camera(float offset_x, float offset_y);
const glm::mat4 get_camera_lookat() const;
const glm::vec3& get_camera_pos() const;
@@ -45,6 +46,8 @@ public:
glm::vec3 get_camera_front() const;
void change_perspective();
bool is_first_person() const;
bool handle_event(const Event& e);
ClientPlayer* player();
};
} // namespace Cubed

View File

@@ -19,18 +19,23 @@ public:
template <TOML::TomlValueType T>
T get(std::string_view key, T default_value) {
if (!m_init) {
load_config();
}
if (auto* node = find_node(m_tbl, key)) {
if (auto value = node->value<T>())
return *value;
}
set(key, default_value);
save_to_file();
return default_value;
}
template <TOML::TomlValueType T> void set(std::string_view key, T&& value) {
if (!m_init) {
load_config();
}
auto pos = key.rfind('.');
toml::table* table;
@@ -52,8 +57,17 @@ public:
save_to_file();
}
std::string get(std::string_view key, const char* default_value) {
return get(key, std::string(default_value));
}
void set(std::string_view key, const char* val) {
set(key, std::string(val));
}
private:
toml::table m_tbl;
bool m_init = false;
const std::string CONGIF_PATH;
const toml::node* find_node(const toml::table& root,
std::string_view path) const;

View File

@@ -1,7 +1,11 @@
#pragma once
#include "Cubed/ui/text.hpp"
#include "Cubed/ui/column_layout.hpp"
#include "Cubed/ui/label.hpp"
#include "Cubed/ui/widget.hpp"
#include <format>
#include <memory>
#include <unordered_map>
namespace Cubed {
@@ -9,16 +13,25 @@ namespace Cubed {
class DebugCollector {
public:
static DebugCollector& get();
static void destory();
DebugCollector();
std::unordered_map<std::size_t, Text>& all_texts();
Text& text(std::string_view name);
void report(std::string_view name, std::string_view content);
void init_text();
void report(const std::string& name, std::string_view content);
void init(int width, int height);
Widget& get_widget();
bool handle_event(const Event& e);
private:
std::unordered_map<std::size_t, Text> m_texts;
ColumnLayout m_widget;
std::unordered_map<std::string, Label*> m_component;
static std::unique_ptr<DebugCollector>& get_ptr();
};
template <typename... Args>
void d_rep(const std::string& key, std::format_string<Args...> fmt,
Args&&... args) {
std::string msg = std::vformat(fmt.get(), std::make_format_args(args...));
DebugCollector::get().report(key, msg);
}
} // namespace Cubed

View File

@@ -6,8 +6,9 @@
namespace Cubed {
class App;
class WorldScene;
class ClientPlayer;
class App;
class DevPanel {
struct ConfigView {
float fov = 70.0f;
@@ -32,12 +33,13 @@ class DevPanel {
};
public:
DevPanel(App& app);
DevPanel(WorldScene& app);
void init();
void render();
private:
App& m_app;
WorldScene& m_world_scene;
Config& m_config;
ConfigView m_config_view;
ClientPlayer* m_player;
@@ -51,7 +53,6 @@ private:
int m_samples_idx = 1;
int m_threads = 1;
int m_chunk_style = 0;
void show_about_table_bar();
void show_biome_table_bar();
void show_time_table_bar();
void show_cave_table_bar();
@@ -62,7 +63,7 @@ private:
void show_server_world_table_bar();
void show_client_world_table_bar();
void show_player_tab_item();
void show_items_tab_item();
// void show_items_tab_item();
void show_shader_tab_item();
void update_config_view();

View File

@@ -42,6 +42,7 @@ struct LookBlock {
struct BlockData {
std::string name;
std::string name_key;
BlockType id = 0;
bool is_liquid = false;
@@ -55,11 +56,11 @@ struct BlockData {
bool is_blend = false;
bool is_transitional = false;
float roughness = 1.0f;
BlockData(BlockType b_id, std::string_view b_name, bool liquid,
bool passable, bool cross_plane, bool transparent, bool gas,
bool discard, bool blend, bool transitional, float r)
: name(b_name), id(b_id), is_liquid(liquid), is_gas(gas),
is_passable(passable), is_cross_plane(cross_plane),
BlockData(std::string_view b_name, std::string_view name_k, BlockType b_id,
bool liquid, bool passable, bool cross_plane, bool transparent,
bool gas, bool discard, bool blend, bool transitional, float r)
: name(b_name), name_key(name_k), id(b_id), is_liquid(liquid),
is_gas(gas), is_passable(passable), is_cross_plane(cross_plane),
is_transparent(transparent), is_discard(discard), is_blend(blend),
is_transitional(transitional), roughness(r) {}
};
@@ -72,7 +73,7 @@ public:
static unsigned sums();
static unsigned cross_plane_sum();
static const std::string& name_form_id(BlockType id);
static std::string local_name(BlockType id);
static bool is_gas(BlockType id);
static bool is_liquid(BlockType id);

View File

@@ -0,0 +1,15 @@
#pragma once
#include "Cubed/ui/color.hpp"
#include <cstdint>
#include <string>
namespace Cubed {
struct ChatMessage {
std::string player;
std::string text;
Color color;
bool system_msg = false;
uint64_t time = 0;
};
} // namespace Cubed

View File

@@ -18,6 +18,7 @@ public:
ChunkGenerator(ServerChunk& chunk);
static void init();
static void init(unsigned seed);
static void reload();
static const unsigned& seed();
static void seed(unsigned s);

View File

@@ -5,8 +5,10 @@
#include "Cubed/gameplay/chunk_pos.hpp"
#include "Cubed/gameplay/game_mode.hpp"
#include "Cubed/gameplay/game_time.hpp"
#include "Cubed/gameplay/item_stack.hpp"
#include "Cubed/gameplay/player.hpp"
#include "Cubed/input.hpp"
#include "Cubed/input/event.hpp"
#include "Cubed/input/input.hpp"
#include <absl/container/flat_hash_set.h>
#include <glm/glm.hpp>
@@ -17,12 +19,21 @@ namespace Cubed {
class ClientWorld;
class ClientPlayer {
public:
static constexpr size_t HOTBAR_SUM = 10;
static constexpr float WALK_SOUND_INTERVAL = 0.45f;
static constexpr float RUN_SOUND_INTERVAL = 0.3f;
using ChunkPosSet = absl::flat_hash_set<ChunkPos, ChunkPos::Hash>;
ClientPlayer(ClientWorld& world);
~ClientPlayer();
bool handle_mouse_button_event(const MouseButtonEvent& e);
bool handle_key_event(const KeyEvent& e);
bool handle_mouse_wheel_event(const MouseWheelEvent& e);
void update_front_vec(float offset_x, float offset_y);
bool update_player_move_state(Key key, KeyAction action);
bool update_scroll(float yoffset);
void update_chunk_set(const ChunkPosSet& set);
const ChunkPosSet& get_chunk_pos_set() const;
ChunkPosSet get_chunk_pos_set();
@@ -36,13 +47,9 @@ public:
const MoveState& get_move_state() const;
void change_mode(GameMode mode);
void hot_reload();
void reload_config();
void set_player_pos(const glm::vec3& pos);
void set_place_block(unsigned id);
void update(float delta_time);
void update_front_vec(float offset_x, float offset_y);
void update_player_move_state(int key, int action);
void update_scroll(double yoffset);
float& max_walk_speed();
float& max_run_speed();
@@ -52,7 +59,7 @@ public:
float& g();
float& fly_y_speed();
unsigned place_block() const;
const ItemStack& get_current_itemstack() const;
void set_gait(Gait gait);
GameMode& game_mode();
@@ -62,7 +69,7 @@ public:
void set_uuid(std::string_view uuid);
std::string get_uuid() const;
const std::string& get_name() const;
void reset_key_status();
void init(std::string_view name);
float yaw() const;
@@ -74,6 +81,11 @@ public:
float distance = 4.0f);
bool is_underwater() const;
void set_underwater(bool u);
void place_block(float dt);
int selected_hotbar() const;
void set_hotbar(int pos, const ItemStack& item);
std::span<const ItemStack, HOTBAR_SUM> get_hotbar() const;
private:
using enum GameMode;
@@ -82,11 +94,13 @@ private:
float m_acceleration = DEFAULT_ACCELERATION;
float m_deceleration = DEFAULT_DECELERATION;
float m_g = DEFAULT_G;
constexpr static float MAX_SPACE_ON_TIME = 0.3f;
static constexpr float MAX_SPACE_ON_TIME = 0.3f;
static constexpr float PLACE_BLOCK_INTERVAL = 0.2f;
float m_place_time = PLACE_BLOCK_INTERVAL;
std::atomic<float> m_yaw = 0.0f;
std::atomic<float> m_pitch = 0.0f;
std::array<ItemStack, HOTBAR_SUM> m_hotbar;
float m_sensitivity = 0.15f;
float m_max_speed = m_max_walk_speed;
@@ -100,7 +114,7 @@ private:
float m_xz_speed = 0.0f;
unsigned m_place_block = 1;
int m_selected_hotbar = 0;
bool m_moving = false;
bool m_sprinting = false;
@@ -119,6 +133,7 @@ private:
std::atomic<Gait> m_gait = Gait::STOP;
MoveState m_move_state{};
MouseState m_mouse_state{};
GameMode m_game_mode = CREATIVE;
std::optional<LookBlock> m_look_block = std::nullopt;
std::string m_name{};
@@ -137,11 +152,15 @@ private:
void update_direction();
void update_lookup_block();
void update_move(float delta_time);
void update_x_move(glm::vec3& player_pos);
void update_y_move(glm::vec3& player_pos);
void update_z_move(glm::vec3& player_pos);
void update_player_chunk();
void play_walk_sound(float dt);
Gait compute_gait() const;
};

View File

@@ -2,11 +2,13 @@
#include "Cubed/audio/audio_engine.hpp"
#include "Cubed/config.hpp"
#include "Cubed/gameplay/block.hpp"
#include "Cubed/gameplay/chat_message.hpp"
#include "Cubed/gameplay/chunk_pos.hpp"
#include "Cubed/gameplay/client_chunk.hpp"
#include "Cubed/gameplay/client_player.hpp"
#include "Cubed/gameplay/game_time.hpp"
#include "Cubed/gameplay/network_client.hpp"
#include "Cubed/input/event.hpp"
#include "Cubed/tools/cubed_random.hpp"
#include "Cubed/tools/priority_thread_pool.hpp"
@@ -41,14 +43,15 @@ struct PlayerRenderData {
Gait gait;
float angle;
};
class WorldScene;
class ClientWorld {
public:
ClientWorld(AudioEngine& auido, Config& config);
ClientWorld(AudioEngine& auido, Config& config, WorldScene& scene);
~ClientWorld();
void init(std::string_view player_name,
std::shared_ptr<NetworkClient> client);
void update(float delta_time);
bool handle_event(const Event& e);
const std::optional<LookBlock>& get_look_block_pos() const;
ClientPlayer& get_player();
const ClientPlayer& get_player() const;
@@ -76,13 +79,15 @@ public:
void rendering_distance(int rendering_distance);
int get_chunk_task_id() const;
void start_client_thread(std::string_view uuid);
void receive_login_rsp(LoginRsp& rsp);
void stop_client_thread();
void start_thread_pool();
void stop_thread_pool();
void change_pool_threads(int threads);
void hot_reload();
void reload_config(bool chunk_build = true);
void request_chunk();
void reset_key_status();
std::vector<glm::vec4>& planes();
const std::vector<const ChunkRenderSnapshot*>& render_snapshots() const;
const std::vector<PlayerRenderData>& render_player_data() const;
@@ -96,7 +101,16 @@ public:
int chunk_size() const;
static AABB get_block_aabb(const glm::ivec3& pos);
AudioEngine& get_audio();
const AudioEngine& get_audio() const;
Config& get_config();
WorldScene& world_scene();
void set_direct_exit();
void receive_chat_message(ChatMsg& msg);
void send_chat_message(ChatMessage& message);
void receive_voice_message(VoiceMsg& msg);
bool enable_voice_chat() const;
template <typename Fn>
void register_ticktimer(std::string_view id, TickType threshold, Fn&& f) {
m_ticktimers.emplace(
@@ -105,6 +119,11 @@ public:
}
private:
struct VoiceMessage {
std::string data;
glm::vec3 pos;
};
std::atomic<bool> m_is_pending_delete_queue_free{false};
std::mutex m_delete_vbo_mutex;
std::mutex m_delete_vao_mutex;
@@ -133,6 +152,7 @@ private:
ChunkHashMap m_chunks;
AudioEngine& m_audio;
Config& m_config;
WorldScene& m_world_scene;
std::vector<glm::vec4> m_planes;
std::jthread m_client_thread;
@@ -141,6 +161,8 @@ private:
tbb::concurrent_queue<std::unique_ptr<ClientChunk>> m_pending_upload_queue;
tbb::concurrent_queue<ChunkPos> m_dirty_chunk_queue;
tbb::concurrent_queue<PendingSound> m_pending_sound;
tbb::concurrent_queue<ChatMessage> m_message_queue;
tbb::concurrent_queue<VoiceMessage> m_voice_queue;
std::deque<ChunkPos> m_dirty_queue;
std::vector<const ChunkRenderSnapshot*> m_render_snapshots;
@@ -148,7 +170,7 @@ private:
tbb::concurrent_unordered_map<std::string, TickTimer> m_ticktimers;
std::unordered_map<std::string, Timer> m_timers;
std::atomic<bool> m_exit_direct{false};
std::atomic<bool> m_game_running{false};
std::atomic<bool> m_receive_exit{false};
std::atomic<int> m_rendering_distance{24};
@@ -157,6 +179,7 @@ private:
std::atomic<bool> m_requesting_chunk{false};
std::atomic<bool> m_is_rebuilding{false};
std::atomic<int> m_chunk_task_id{0};
std::atomic<bool> m_voice_chat{true};
std::shared_ptr<NetworkClient> m_client;
ChunkLoadStyle m_chunk_load_style{ChunkLoadStyle::CENTER};

View File

@@ -0,0 +1,8 @@
#pragma once
#include "Cubed/gameplay/block.hpp"
namespace Cubed {
struct ItemStack {
BlockType type = 0;
size_t sum = 0;
};
} // namespace Cubed

View File

@@ -19,6 +19,9 @@ public:
void start(std::string ip, int port = 25530);
bool is_connected() const;
bool is_connect_error() const;
std::string get_error_string() const;
void clear_error();
ClientWorld& world();
private:
struct Task {
@@ -53,6 +56,8 @@ private:
std::atomic<bool> m_closed{false};
std::atomic<bool> m_connected{false};
std::atomic<bool> m_connect_error{false};
mutable std::mutex m_error_string_mutex;
std::string m_error_string;
// ClientWorld is managed by App
ClientWorld& m_world;
std::atomic_uint64_t m_sequence{0};
@@ -61,5 +66,6 @@ private:
asio::awaitable<void> read_loop();
void do_write();
void set_error(std::string_view error);
};
} // namespace Cubed

View File

@@ -61,6 +61,8 @@ enum class PacketEnum : uint16_t {
BLOCK_CHANGE_RSP = 3004,
S2C_CLEAR_ALL_CHUNKS = 3005,
UPDATE_TIME = 3006,
CHAT_MSG = 4001,
VOICE_MSG = 4002,
PING = 9001,
PONG = 9002
@@ -121,6 +123,12 @@ template <> constexpr uint16_t get_packet_id<Pong>() {
template <> constexpr uint16_t get_packet_id<PlayerWaterSound>() {
return std::to_underlying(PacketEnum::PLAYER_WATER_SOUND);
}
template <> constexpr uint16_t get_packet_id<ChatMsg>() {
return std::to_underlying(PacketEnum::CHAT_MSG);
}
template <> constexpr uint16_t get_packet_id<VoiceMsg>() {
return std::to_underlying(PacketEnum::VOICE_MSG);
}
template <typename T>
requires std::derived_from<T, google::protobuf::Message>

View File

@@ -10,7 +10,9 @@
#include "Cubed/gameplay/server_player.hpp"
#include "Cubed/tools/priority_thread_pool.hpp"
#include "Cubed/tools/recent_queue.hpp"
#include "Cubed/tools/sensitive_filter.hpp"
#include "Cubed/tools/thread_pool.hpp"
#include "Cubed/ui/color.hpp"
#include "world/block_change.pb.h"
#include <absl/container/flat_hash_set.h>
@@ -80,6 +82,8 @@ public:
void handle_chunk_req(int task_id, const std::string& uuid, ChunkPos pos);
void handle_block_change(const BlockChangeReq& req);
void handle_chat_message(ChatMsg& msg);
void handle_voice_message(VoiceMsg& msg);
int chunk_size() const;
template <typename Fn>
void register_timer(std::string_view id, TickType threshold, Fn&& f) {
@@ -128,6 +132,9 @@ private:
CaveCarver m_cave_carcer;
RiverWorm m_river_worm;
std::atomic<bool> m_enable_filter{false};
std::atomic<bool> m_voice_chat{true};
SensitiveFilter m_filter;
std::jthread m_gen_thread;
std::jthread m_server_thread;
@@ -189,5 +196,8 @@ private:
std::atomic<std::shared_ptr<PriorityThreadPool>>& thread_pool,
int threads);
void send_server_stop();
void boardcast_message(const std::string& name, const std::string& message,
Color color = Color::WHITE, bool system_msg = false);
};
} // namespace Cubed

View File

@@ -0,0 +1,63 @@
#pragma once
#include "Cubed/input/key.hpp"
#include "Cubed/input/mouse.hpp"
#include <string>
#include <variant>
namespace Cubed {
struct MouseMoveEvent {
float xpos;
float ypos;
float xrel;
float yrel;
MouseMoveEvent(float x, float y, float dx, float dy)
: xpos(x), ypos(y), xrel(dx), yrel(dy) {}
};
struct MouseButtonEvent {
MouseKey key;
KeyAction action;
MouseButtonEvent(MouseKey k, KeyAction a) : key(k), action(a) {}
};
struct MouseWheelEvent {
float offset;
MouseWheelEvent(float o) : offset(o) {}
};
struct KeyEvent {
Key key;
KeyAction action;
KeyEvent(Key k, KeyAction a) : key(k), action(a) {}
};
struct TextInputEvent {
std::string text;
TextInputEvent(std::string t) : text(std::move(t)) {}
};
struct WindowResizeEvent {
int width;
int height;
};
struct FrameBufferResizeEvent {
int width;
int height;
};
using Event =
std::variant<MouseMoveEvent, MouseButtonEvent, MouseWheelEvent, KeyEvent,
TextInputEvent, WindowResizeEvent, FrameBufferResizeEvent>;
template <class... T> struct Overloaded : T... {
using T::operator()...;
};
template <class... T> Overloaded(T...) -> Overloaded<T...>;
} // namespace Cubed

View File

@@ -1,7 +1,5 @@
#pragma once
#include <GLFW/glfw3.h>
namespace Cubed {
struct MoveState {
@@ -18,19 +16,4 @@ struct MouseState {
bool right = false;
};
struct KeyState {
bool r = false;
};
struct InputState {
MoveState move_state;
MouseState mouse_state;
KeyState key_state;
};
namespace Input {
InputState& get_input_state();
}
} // namespace Cubed

129
include/Cubed/input/key.hpp Normal file
View File

@@ -0,0 +1,129 @@
#pragma once
#ifdef DELETE
#undef DELETE
#endif
namespace Cubed {
enum class Key {
// Letter keys
A,
B,
C,
D,
E,
F,
G,
H,
I,
J,
K,
L,
M,
N,
O,
P,
Q,
R,
S,
T,
U,
V,
W,
X,
Y,
Z,
// Digit keys (main keyboard area)
DIGIT_0,
DIGIT_1,
DIGIT_2,
DIGIT_3,
DIGIT_4,
DIGIT_5,
DIGIT_6,
DIGIT_7,
DIGIT_8,
DIGIT_9,
// Function keys
F1,
F2,
F3,
F4,
F5,
F6,
F7,
F8,
F9,
F10,
F11,
F12,
// Control keys
BACKSPACE,
TAB,
ENTER,
ESCAPE,
SPACE,
CAPS_LOCK,
NUM_LOCK,
SCROLL_LOCK,
// Modifier keys
LEFT_SHIFT,
RIGHT_SHIFT,
LEFT_CTRL,
RIGHT_CTRL,
LEFT_ALT,
RIGHT_ALT,
LEFT_SUPER,
RIGHT_SUPER, // Windows / Command 键
// Navigation keys
INSERT,
DELETE,
HOME,
END,
PAGE_UP,
PAGE_DOWN,
LEFT,
RIGHT,
UP,
DOWN,
// Lock and system keys
PRINT_SCREEN,
PAUSE,
// Main keyboard area symbol keys
GRAVE_ACCENT, // `
MINUS, // -
EQUALS, // =
LEFT_BRACKET, // [
RIGHT_BRACKET, // ]
BACKSLASH, // \ /
SEMICOLON, // ;
APOSTROPHE, // '
COMMA, // ,
PERIOD, // .
SLASH, // /
// Numpad area
NUMPAD_0,
NUMPAD_1,
NUMPAD_2,
NUMPAD_3,
NUMPAD_4,
NUMPAD_5,
NUMPAD_6,
NUMPAD_7,
NUMPAD_8,
NUMPAD_9,
NUMPAD_ADD, // +
NUMPAD_SUBTRACT, // -
NUMPAD_MULTIPLY, // *
NUMPAD_DIVIDE, // /
NUMPAD_DECIMAL, // .
NUMPAD_ENTER
};
enum class KeyAction { PRESS, RELEASE, REPEAT };
} // namespace Cubed

View File

@@ -0,0 +1,26 @@
#pragma once
namespace Cubed {
enum class MouseKey {
LEFT_BUTTON,
RIGHT_BUTTON,
MIDDLE_BUTTON,
BACK_BUTTON, // Side button back
FORWARD_BUTTON, // Side button forward
/*
WHEEL_UP,
WHEEL_DOWN,
WHEEL_LEFT,
WHEEL_RIGHT,
EXTRA_BUTTON_1, // Additional programmable buttons
EXTRA_BUTTON_2,
EXTRA_BUTTON_3,
EXTRA_BUTTON_4,
EXTRA_BUTTON_5
*/
};
}

View File

@@ -0,0 +1,49 @@
#pragma once
#include <format>
#include <string>
#include <string_view>
#include <unordered_map>
namespace Cubed {
struct FormatArg {
std::string name;
std::string value;
};
template <typename T> FormatArg arg(std::string_view name, T&& value) {
return {std::string(name), std::format("{}", std::forward<T>(value))};
}
class Localization {
public:
Localization();
static Localization& instance();
void load_language(std::string_view language);
bool has(const std::string& key) const;
template <typename... Args>
std::string translate(const std::string& key, Args&&... args) {
if constexpr (sizeof...(Args) == 0)
return std::string{lookup(key)};
std::string fmt{lookup(key)};
(replace_all(fmt, "{" + args.name + "}", args.value), ...);
return fmt;
}
private:
std::unordered_map<std::string, std::string> m_translations;
std::string m_current_lang{"en_US"};
std::string_view lookup(const std::string& key) const;
static void replace_all(std::string& str, std::string_view from,
std::string_view to);
};
template <typename... Args>
std::string tr(const std::string& key, Args&&... args) {
return Localization::instance().translate(key, std::forward<Args>(args)...);
}
template <typename T>
FormatArg tr_arg(const std::string& name, const std::string& key) {
return {std::string(name), tr(key)};
}
} // namespace Cubed

View File

@@ -188,13 +188,14 @@ constexpr float CUBE_VER[24] = {0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0,
constexpr int OUTLINE_CUBE_INDICES[24] = {0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6,
6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7};
constexpr float SQUARE_VERTICES[6][2] = {
{-0.5f, -0.5f}, // bottom left
{-0.5f, 0.5f}, // top left
{0.5f, 0.5f}, // top right
{0.5f, 0.5f}, // top right
{0.5f, -0.5f}, // bottom right
{-0.5f, -0.5f} // bottom left
constexpr float SQUARE_VERTICES_TOP_LEFT[6][2] = {
{0.0f, 0.0f}, // top left
{0.0f, 1.0f}, // bottom left
{1.0f, 1.0f}, // bottom right
{1.0f, 1.0f}, // bottom right
{1.0f, 0.0f}, // top right
{0.0f, 0.0f} // top left
};
constexpr float SQUARE_TEXTURE_POS[6][2] = {

View File

@@ -5,6 +5,7 @@
#include <glad/glad.h>
namespace Cubed {
class ClientWorld;
class Renderer;
class PlayerRenderer {
public:
@@ -12,8 +13,9 @@ public:
PlayerRenderer(Renderer& renderer);
~PlayerRenderer();
void init();
void render(const Shader& shader);
void shadow_render(const Shader& shader, glm::mat4& light_matrix);
void render(const Shader& shader, ClientWorld& world);
void shadow_render(const Shader& shader, glm::mat4& light_matrix,
ClientWorld& world);
private:
struct PlayerVertex {

View File

@@ -2,6 +2,7 @@
#include "Cubed/config.hpp"
#include "Cubed/constants.hpp"
#include "Cubed/input/event.hpp"
#include "Cubed/primitive_data.hpp"
#include "Cubed/render/player_renderer.hpp"
#include "Cubed/render/shader_manager.hpp"
@@ -9,13 +10,13 @@
#include "Cubed/render/vertex_buffer.hpp"
#include "Cubed/render/world_renderer.hpp"
#include "Cubed/shader.hpp"
#include "Cubed/ui/text.hpp"
#include "Cubed/ui/image.hpp"
#include "Cubed/ui/label.hpp"
#include "Cubed/ui/rect.hpp"
#include <glm/glm.hpp>
#include <vector>
namespace Cubed {
class Camera;
class TextureManager;
class ClientWorld;
class DevPanel;
@@ -23,18 +24,23 @@ class Renderer {
public:
constexpr static int NUM_VAO = 7;
Renderer(const Camera& camera, ClientWorld& world,
const TextureManager& texture_manager, DevPanel& dev_panel,
Config& config);
Renderer(TextureManager& texture_manager, Config& config);
~Renderer();
void hot_reload();
void init(bool debug_on);
void reload_config();
void init();
const Shader& get_shader(const std::string& name) const;
void render();
void begin_frame();
void end_frame();
void render_world(ClientWorld& world);
void render_lable(const Label& label);
void render_image(const Image& image);
void render_rect(const Rect& rect);
void begin_render_ui();
void end_render_ui();
void update(float delta_time);
void update_fov(float fov);
void update_proj_matrix(float aspect, float width, float height);
void updata_framebuffer(int width, int height);
float& ambient_strength();
bool& discard_transparent();
@@ -58,24 +64,23 @@ public:
float& underwater_fog_density();
float& water_density();
const Camera& camera() const;
const ClientWorld& world() const;
ClientWorld& world();
const glm::mat4& world_proj_matrix() const;
const TextureManager& texture_mamger() const;
float delta_time() const;
float height() const;
float width() const;
float window_height() const;
float window_width() const;
float frame_height() const;
float frame_width() const;
const glm::mat4& p_mat() const;
const std::vector<VertexArray>& vao() const;
void render_dev_panel(DevPanel& dev_panel);
bool handle_event(const Event& e);
private:
const Camera& m_camera;
DevPanel& m_dev_panel;
const TextureManager& m_texture_manager;
ClientWorld& m_world;
TextureManager& m_texture_manager;
bool m_init = false;
@@ -84,8 +89,11 @@ private:
float m_delta_time = 0.0f;
float m_width = 0.0f;
float m_height = 0.0f;
float m_frame_width = 0.0f;
float m_frame_height = 0.0f;
float m_window_width = 0.0f;
float m_window_height = 0.0f;
glm::mat4 m_world_proj_matrix;
@@ -97,14 +105,13 @@ private:
std::unique_ptr<VertexBuffer> m_quad_vbo;
glm::mat4 m_ui_proj_matrix;
glm::mat4 m_ui_model_matrix;
ShaderManager m_shaders;
/*
0 - quad vao
0 - quad vao (center)
1 - sky vao
2 - outline vao
3 - ui vao
3 - ui vao (top-left)
4 - text vao
*/
std::vector<VertexArray> m_vao;
@@ -112,16 +119,12 @@ private:
WorldRenderer m_world_renderer;
Config& m_config;
bool handle_window_resize_event(const WindowResizeEvent& e);
bool handle_frame_buffer_resize_event(const FrameBufferResizeEvent& e);
void updata_framebuffer(int width, int height);
void init_quad();
void init_text();
void day_night_calculation();
void render_sky();
void render_text();
void render_ui();
void render_dev_panel();
};
} // namespace Cubed

View File

@@ -35,6 +35,7 @@ enum TextureFormat : GLenum {
RGBA16F = GL_RGBA16F,
RED = GL_RED,
RGBA = GL_RGBA,
R8 = GL_R8,
RGB = GL_RGB,
RGBA8 = GL_RGBA8,
@@ -60,12 +61,12 @@ public:
void tex_image_2d(TextureFormat internalformat, TextureFormat format,
GLenum type, const void* data, GLsizei width,
GLsizei height, GLint level = 0, GLint border = 0) const;
GLsizei height, GLint level = 0, GLint border = 0);
void tex_image_3d(TextureFormat internalformat, TextureFormat format,
GLenum type, const void* data, GLsizei width,
GLsizei height, GLsizei depth, GLint level = 0,
GLint border = 0) const;
GLint border = 0);
void tex_sub_image_3d(TextureFormat format, GLenum type, const void* data,
GLint xoffset, GLint yoffset, GLint zoffset,
@@ -81,14 +82,17 @@ public:
void set_nearest() const;
void set_repeat(bool r = true, bool s = true, bool t = true) const;
void set_clamp_to_border(bool r = true, bool s = true, bool t = true) const;
void set_clamp_to_edge(bool r = true, bool s = true, bool t = true) const;
void set_clamp_to_edge(bool r = false, bool s = true, bool t = true) const;
TextureType type() const;
float width() const;
float height() const;
private:
GLuint m_id = 0;
float m_width = 0;
float m_height = 0;
const TextureType M_TYPE;
GLenum get_gl_texture_type() const;
};
} // namespace Cubed

View File

@@ -39,7 +39,7 @@ public:
WorldRenderer& operator=(const WorldRenderer&) = delete;
WorldRenderer& operator=(WorldRenderer&&) = delete;
void init();
void render();
void render(ClientWorld& world);
void updata_framebuffer(int width, int height);
float& ambient_strength();
@@ -121,27 +121,26 @@ private:
glm::mat4 view_matrix;
ClientWorld& m_world;
const Camera& m_camera;
const TextureManager& m_texture_manager;
void day_night_calculation();
void day_night_calculation(ClientWorld& world);
void render_sky();
void render_sky(ClientWorld& world);
void render_world();
void render_world(ClientWorld& world);
void shadow_map_generate();
void shadow_map_generate(ClientWorld& world);
void render_underwater();
void render_outline();
void render_player();
void render_underwater(ClientWorld& world);
void render_outline(ClientWorld& world);
void render_player(ClientWorld& world);
void render_normal_block(const glm::mat4& model_mat,
const glm::mat4& mv_mat,
const glm::mat4& norm_mat);
const glm::mat4& mv_mat, const glm::mat4& norm_mat,
ClientWorld& world);
void render_transparent_block(const glm::mat4& mv_mat,
const glm::mat4& norm_mat);
const glm::mat4& norm_mat,
ClientWorld& world);
glm::vec3 quantize_sun_direction(const glm::vec3& sundir,
float angle_step_deg) const;

View File

@@ -0,0 +1,21 @@
#pragma once
#include "Cubed/scene/scene.hpp"
#include "Cubed/ui/credits_ui.hpp"
namespace Cubed {
class SceneManager;
class CreditsScene : public Scene {
public:
CreditsScene(SceneManager& scene_manager);
void update(float dt) override;
void render(Renderer& renderer) override;
bool handle_event(const Event& e) override;
void on_enter() override;
void on_re_enter() override;
SceneManager& scene_manager();
private:
SceneManager& m_scene_manager;
CreditsUI m_ui;
};
} // namespace Cubed

View File

@@ -0,0 +1,29 @@
#pragma once
#include "Cubed/scene/scene.hpp"
#include "Cubed/ui/host_game_ui.hpp"
namespace Cubed {
class SceneManager;
class HostGameScene : public Scene {
public:
HostGameScene(SceneManager& scene_manager);
~HostGameScene();
HostGameScene(const HostGameScene&) = delete;
HostGameScene(HostGameScene&&) = delete;
HostGameScene& operator=(const HostGameScene&) = delete;
HostGameScene& operator=(HostGameScene&&) = delete;
void update(float dt) override;
void render(Renderer& renderer) override;
bool handle_event(const Event& e) override;
void on_enter() override;
void on_leave() override;
void on_re_enter() override;
SceneManager& scene_manager();
private:
SceneManager& m_scene_manager;
HostGameUI m_ui;
};
} // namespace Cubed

View File

@@ -0,0 +1,28 @@
#pragma once
#include "Cubed/scene/scene.hpp"
#include "Cubed/ui/join_game_ui.hpp"
namespace Cubed {
class SceneManager;
class JoinGameScene : public Scene {
public:
JoinGameScene(const JoinGameScene&) = delete;
JoinGameScene(JoinGameScene&&) = delete;
JoinGameScene& operator=(const JoinGameScene&) = delete;
JoinGameScene& operator=(JoinGameScene&&) = delete;
JoinGameScene(SceneManager& scene_manager);
void update(float dt) override;
void render(Renderer& renderer) override;
bool handle_event(const Event& e) override;
void on_enter() override;
void on_leave() override;
void on_re_enter() override;
SceneManager& scene_manager();
private:
SceneManager& m_scene_manager;
JoinGameUI m_ui;
};
} // namespace Cubed

View File

@@ -0,0 +1,28 @@
#pragma once
#include "Cubed/scene/scene.hpp"
#include "Cubed/ui/main_menu_ui_manager.hpp"
namespace Cubed {
class SceneManager;
class MainMenuScene : public Scene {
public:
MainMenuScene(const MainMenuScene&) = delete;
MainMenuScene(MainMenuScene&&) = delete;
MainMenuScene& operator=(const MainMenuScene&) = delete;
MainMenuScene& operator=(MainMenuScene&&) = delete;
MainMenuScene(SceneManager& scene_manager);
~MainMenuScene();
void update(float dt) override;
void render(Renderer& renderer) override;
bool handle_event(const Event& e) override;
void on_enter() override;
void on_leave() override;
void on_re_enter() override;
SceneManager& scene_manager();
private:
SceneManager& m_scene_manager;
MainMenuUIManager m_ui_manager;
};
} // namespace Cubed

View File

@@ -0,0 +1,50 @@
#pragma once
#include "Cubed/input/event.hpp"
namespace Cubed {
class Renderer;
enum class SceneType {
MAIN_MENU,
WORLD,
CREDITS,
SETTINGS,
HOST_GAME,
JOIN_GAME
};
class Scene {
public:
Scene() = default;
Scene(const Scene&) = delete;
Scene(Scene&&) = delete;
Scene& operator=(const Scene&) = delete;
Scene& operator=(Scene&&) = delete;
virtual ~Scene() = default;
virtual void update(float dt) = 0;
virtual void render(Renderer& renderer) = 0;
virtual bool handle_event(const Event& e) = 0;
virtual void on_enter() {};
virtual void on_leave() {};
virtual void on_re_enter() {};
protected:
virtual bool handle_mouse_move_event(const MouseMoveEvent&) {
return false;
}
virtual bool handle_mouse_button_event(const MouseButtonEvent&) {
return false;
}
virtual bool handle_window_resize_event(const WindowResizeEvent&) {
return false;
}
virtual bool handle_mouse_wheel_event(const MouseWheelEvent&) {
return false;
}
virtual bool handle_key_event(const KeyEvent&) { return false; }
virtual bool handle_text_input_event(const TextInputEvent&) {
return false;
}
};
} // namespace Cubed

View File

@@ -0,0 +1,59 @@
#pragma once
#include "Cubed/scene/scene.hpp"
#include <memory>
#include <optional>
#include <stack>
#include <vector>
namespace Cubed {
class App;
struct WorldSceneParam {
bool host_game = true;
std::optional<unsigned> seed = std::nullopt;
std::string ip{"127.0.0.1"};
int port = 25530;
};
class SceneManager {
public:
SceneManager(const SceneManager&) = delete;
SceneManager(SceneManager&&) = delete;
SceneManager& operator=(const SceneManager&) = delete;
SceneManager& operator=(SceneManager&&) = delete;
SceneManager(App& app);
~SceneManager();
void update(float dt);
void render(Renderer& renderer);
bool handle_event(const Event& e);
void request_change(SceneType type);
void request_push(SceneType type);
void request_pop();
App& app();
WorldSceneParam& world_scene_param();
private:
enum class OperationType { PUSH, POP, CHANGE };
struct SceneOperation {
OperationType type;
std::optional<SceneType> scene;
};
App& m_app;
WorldSceneParam m_world_param;
std::vector<std::unique_ptr<Scene>> m_pending_delete_scene;
std::optional<SceneOperation> m_operation;
std::stack<std::unique_ptr<Scene>> m_scenes;
void process_operation();
void change(SceneType type);
void push(SceneType type);
void pop(bool re_enter = true);
std::unique_ptr<Scene> create_scene(SceneType);
};
} // namespace Cubed

View File

@@ -0,0 +1,42 @@
#pragma once
#include "Cubed/scene/scene.hpp"
#include "Cubed/ui/settings_ui.hpp"
namespace Cubed {
struct SliderVariable {
float fov = 70.0f;
float mouse_sensitivity = 0.15f;
float sfx = 1.0f;
float music = 0.5f;
float player_voice = 1.0f;
int rendering_distance = 24;
};
class SceneManager;
class SettingsScene : public Scene {
public:
SettingsScene(const SettingsScene&) = delete;
SettingsScene(SettingsScene&&) = delete;
SettingsScene& operator=(const SettingsScene&) = delete;
SettingsScene& operator=(SettingsScene&&) = delete;
SettingsScene(SceneManager& scene_manager);
~SettingsScene();
void update(float dt) override;
void render(Renderer& renderer) override;
bool handle_event(const Event& e) override;
void on_enter() override;
void on_leave() override;
void on_re_enter() override;
SceneManager& scene_manager();
SliderVariable& slider_variable();
void set_texture_reload(bool reload);
private:
void save_and_apply();
SceneManager& m_scene_manager;
SliderVariable m_slider_variable;
SettingsUI m_settings_ui;
bool m_need_texture_reload = false;
};
} // namespace Cubed

View File

@@ -0,0 +1,72 @@
#pragma once
#include "Cubed/argument.hpp"
#include "Cubed/camera.hpp"
#include "Cubed/dev_panel.hpp"
#include "Cubed/gameplay/client_world.hpp"
#include "Cubed/gameplay/network_server.hpp"
#include "Cubed/scene/scene.hpp"
#include "Cubed/ui/error_ui.hpp"
#include "Cubed/ui/inventory_ui.hpp"
#include "Cubed/ui/pasue_menu_ui_manager.hpp"
#include "Cubed/ui/world_ui_manager.hpp"
namespace Cubed {
enum class VoiceInputType { OFF, PTT, ALWAYS };
class SceneManager;
class WorldScene : public Scene {
public:
WorldScene(const WorldScene&) = delete;
WorldScene(WorldScene&&) = delete;
WorldScene& operator=(const WorldScene&) = delete;
WorldScene& operator=(WorldScene&&) = delete;
WorldScene(SceneManager& scene_manager);
~WorldScene();
void update(float dt) override;
void render(Renderer& renderer) override;
bool handle_event(const Event& e) override;
void on_enter() override;
void on_leave() override;
void on_re_enter() override;
Camera& camera();
SceneManager& scene_manager();
ClientWorld& client_world();
ServerWorld& server_world();
bool pause() const;
void set_pause(bool pause);
void set_error(std::string_view error);
void set_mouse(bool pause);
void set_chatting(bool chatting, bool send);
// Not thread safe
void handle_chat_message(ChatMessage& message);
bool is_recording() const;
private:
enum class PauseUI { PAUSE_MENU, INVENTORY };
SceneManager& m_scene_manager;
DevPanel m_dev_panel;
Camera m_camera;
NetworkServer m_server;
std::shared_ptr<NetworkClient> m_client;
ClientWorld m_client_world;
bool m_paused = false;
bool m_show_hud = true;
bool m_show_dev_pannel = true;
bool m_chatting = false;
PauseMenuUIManager m_pasue_menu;
InventoryUI m_inventory_ui;
PauseUI m_paused_ui = PauseUI::PAUSE_MENU;
WorldUIManager m_hud_ui;
ErrorUI m_error_ui;
const Argument& m_argument;
VoiceInputType m_input_type;
bool handle_mouse_move_event(const MouseMoveEvent& e) override;
bool handle_mouse_button_event(const MouseButtonEvent& e) override;
bool handle_window_resize_event(const WindowResizeEvent& e) override;
bool handle_mouse_wheel_event(const MouseWheelEvent& e) override;
bool handle_key_event(const KeyEvent& e) override;
bool handle_text_input_event(const TextInputEvent&) override;
void load_config();
};
} // namespace Cubed

View File

@@ -40,6 +40,9 @@ public:
} else if constexpr (is_same_v<dT, glm::mat4>) {
glUniformMatrix4fv(loc(location), 1, GL_FALSE,
glm::value_ptr(value));
} else if constexpr (is_same_v<dT, glm::vec4>) {
glUniform4fv(loc(location), 1, glm::value_ptr(value));
} else {
static_assert(always_false<dT>::value, "Unknown Type");
}

View File

@@ -1,6 +1,7 @@
#pragma once
#include "Cubed/config.hpp"
#include "Cubed/gameplay/block.hpp"
#include "Cubed/input/event.hpp"
#include "Cubed/render/texture.hpp"
#include <glad/glad.h>
@@ -15,10 +16,10 @@ private:
std::unique_ptr<Texture> m_block_status_array;
std::unique_ptr<Texture> m_texture_array;
std::unique_ptr<Texture> m_cross_plane_array;
std::unique_ptr<Texture> m_ui_array;
std::unique_ptr<Texture> m_normal_texture_array;
std::unique_ptr<Texture> m_skin;
std::vector<std::unique_ptr<Texture>> m_item_textures;
std::unordered_map<std::string, std::unique_ptr<Texture>> m_ui_map;
GLfloat m_max_aniso = 0.0f;
Config& m_config;
int m_aniso = 1;
@@ -27,7 +28,7 @@ private:
void load_block_texture(unsigned block_id);
void load_block_item_texture(unsigned id);
void load_cross_plane_texture(unsigned id);
void load_ui_texture(unsigned id);
const Texture* load_image_texture(const std::string& path);
void load_pbr_texture(unsigned id);
void init_item();
void init_block();
@@ -35,6 +36,7 @@ private:
void init_block_status();
void init_skin();
void hot_reload();
bool handle_key_event(const KeyEvent& e);
public:
TextureManager(Config& config);
@@ -44,16 +46,16 @@ public:
const Texture* get_block_status_array() const;
const Texture* get_texture_array() const;
const Texture* get_cross_plane_array() const;
const Texture* get_ui_array() const;
const Texture* get_image_texture(const std::string& path);
const Texture* get_pbr_texture() const;
const std::vector<std::unique_ptr<Texture>>& item_textures() const;
const std::vector<std::unique_ptr<Texture>>& get_item_textures() const;
const Texture* get_skin() const;
// Must call after MapTable::init_map() and glfwMakeContextCurrent(window);
void init_texture();
void need_reload();
void update();
int max_aniso() const;
bool handle_event(const Event& e);
};
} // namespace Cubed

View File

@@ -0,0 +1,14 @@
#pragma once
#include <cstdlib> // IWYU pragma: keep
namespace Cubed {
namespace Tools {
inline void set_env(const char* name, const char* value) {
#ifdef _WIN32
_putenv_s(name, value);
#else
setenv(name, value, 1);
#endif
}
} // namespace Tools
} // namespace Cubed

View File

@@ -9,6 +9,7 @@
#include <glad/glad.h>
#include <glm/glm.hpp>
#include <hb-ft.h>
#include <string>
#include <unordered_map>
@@ -22,33 +23,57 @@ struct Character {
GLuint advance;
};
struct Glyph {
glm::vec2 uv_min;
glm::vec2 uv_max;
glm::ivec2 size;
glm::ivec2 bearing;
int layer = 0;
};
struct TextMesh {
std::vector<Vertex2D> vertices;
float width;
float height;
float min_x;
float min_y;
};
class Shader;
class Font {
public:
static constexpr int CELL_SIZE = 64;
Font();
~Font();
static Font& get();
static std::vector<Vertex2D> vertices(const std::string& text,
float x = 0.0f, float y = 0.0f,
float scale = 1.0f);
static const Texture* text_texture();
static void destroy();
TextMesh vertices(const std::string& text);
const Texture* text_texture();
static const std::string& font_path();
static float text_width(const std::string& text);
private:
FT_Library m_ft;
FT_Face m_face;
hb_font_t* m_hb_font;
int m_next_layer = 0;
float m_texture_width = 64;
float m_texture_height = 64;
static inline std::unique_ptr<Texture> m_text_texture;
std::unique_ptr<Texture> m_text_texture;
static inline std::string m_font_path{ASSETS_PATH
"fonts/IBMPlexSans-Regular.ttf"};
std::unordered_map<char8_t, Character> m_characters;
void load_character(char8_t c);
void setup_font_character();
"fonts/unifont_t-17.0.05.otf"};
std::unordered_map<uint32_t, Glyph> m_cache;
int m_max_layers = 0;
Glyph& load_glyph(uint32_t glyph_index);
void upload_glyph(Glyph& glyph, const unsigned char* buffer);
static std::unique_ptr<Font>& get_ptr();
};
} // namespace Cubed

View File

@@ -1,85 +1,184 @@
#pragma once
#include <atomic>
#include <chrono>
#include <filesystem>
#include <format>
#include <fstream>
#include <iostream>
#include <source_location>
#include <memory>
#include <mutex>
#include <print>
#include <shared_mutex>
#include <string>
#include <syncstream>
#include <utility>
#ifdef DEBUG
#undef DEBUG
#endif
#ifdef INFO
#undef INFO
#endif
#ifdef ERROR
#undef ERROR
#endif
#ifdef WARN
#undef WARN
#endif
namespace Cubed {
namespace Logger {
enum class Level { L_TRACE, L_DEBUG, L_INFO, L_ERROR, L_WARN };
class Logger {
public:
enum class Level { DEBUG = 0, INFO = 1, WARN = 2, ERROR = 3 };
static constexpr Level get_level(int i) {
switch (i) {
case std::to_underlying(Level::DEBUG):
return Level::DEBUG;
template <typename... Args>
inline void info(std::format_string<Args...> fmt, Args&&... args) {
auto now_time = std::chrono::time_point_cast<std::chrono::seconds>(
std::chrono::system_clock::now());
std::string msg = std::vformat(fmt.get(), std::make_format_args(args...));
std::osyncstream(std::cout)
<< "\033[1;32m" << std::format("[INFO][{:%Y-%m-%d %H:%M:%S}]", now_time)
<< msg << "\033[0m"
<< "\n";
}
template <typename... Args>
inline void error(std::format_string<Args...> fmt, Args&&... args) {
auto now_time = std::chrono::time_point_cast<std::chrono::seconds>(
std::chrono::system_clock::now());
std::string msg = std::vformat(fmt.get(), std::make_format_args(args...));
std::osyncstream(std::cerr)
<< "\033[1;31m"
<< std::format("[ERROR][{:%Y-%m-%d %H:%M:%S}]", now_time) << msg
<< "\033[0m"
<< "\n";
}
template <typename... Args>
inline void warn(std::format_string<Args...> fmt, Args&&... args) {
auto now_time = std::chrono::time_point_cast<std::chrono::seconds>(
std::chrono::system_clock::now());
std::string msg = std::vformat(fmt.get(), std::make_format_args(args...));
std::osyncstream(std::cout)
<< "\033[1;33m" << std::format("[WARN][{:%Y-%m-%d %H:%M:%S}]", now_time)
<< msg << "\033[0m"
<< "\n";
}
template <typename... Args>
inline void log(Level level, std::source_location loc,
std::format_string<Args...> fmt, Args&&... args) {
auto now_time = std::chrono::time_point_cast<std::chrono::seconds>(
std::chrono::system_clock::now());
std::string msg = std::vformat(fmt.get(), std::make_format_args(args...));
switch (level) {
case Logger::Level::L_TRACE:
std::osyncstream(std::cout)
<< "\033[1;34m"
<< std::format("[TRACE][{:%Y-%m-%d %H:%M:%S}]", now_time) << "["
<< loc.file_name() << ":" << loc.line() << "]"
<< "[" << loc.function_name() << "]" << msg << "\033[0m"
<< "\n";
break;
case Logger::Level::L_DEBUG:
std::osyncstream(std::cout)
<< "\033[1;34m"
<< std::format("[DEBUG][{:%Y-%m-%d %H:%M:%S}]", now_time) << msg
<< "\033[0m"
<< "\n";
break;
case Logger::Level::L_INFO:
info(fmt, std::forward<Args>(args)...);
break;
case Logger::Level::L_WARN:
warn(fmt, std::forward<Args>(args)...);
break;
case Logger::Level::L_ERROR:
error(fmt, std::forward<Args>(args)...);
break;
case std::to_underlying(Level::INFO):
return Level::INFO;
case std::to_underlying(Level::WARN):
return Level::WARN;
case std::to_underlying(Level::ERROR):
return Level::ERROR;
default:
return Level::INFO;
}
}
Logger() { m_file_stream = std::make_unique<std::ofstream>(); }
~Logger() { m_file_stream.reset(); }
static Logger& instance() {
static Logger inst;
return inst;
}
static void set_level(Level level) { instance().m_level = level; }
static void set_console_write(bool write) {
instance().m_console_write = write;
}
}
} // namespace Logger
static void set_file_write(bool write) { instance().m_file_write = write; }
static void set_logs_path(const std::string& path) {
std::unique_lock lock(instance().m_mutex);
instance().m_logs_path = path;
}
template <typename... Args>
static void info(std::format_string<Args...> fmt, Args&&... args) {
instance().log(Level::INFO, fmt, std::forward<Args>(args)...);
}
template <typename... Args>
static void error(std::format_string<Args...> fmt, Args&&... args) {
instance().log(Level::ERROR, fmt, std::forward<Args>(args)...);
}
template <typename... Args>
static void warn(std::format_string<Args...> fmt, Args&&... args) {
instance().log(Level::WARN, fmt, std::forward<Args>(args)...);
}
template <typename... Args>
static void debug(std::format_string<Args...> fmt, Args&&... args) {
instance().log(Level::DEBUG, fmt, std::forward<Args>(args)...);
}
template <typename... Args>
void log(Level level, std::format_string<Args...> fmt, Args&&... args) {
auto now_time = std::chrono::time_point_cast<std::chrono::seconds>(
std::chrono::system_clock::now());
if (level < m_level) {
return;
}
auto msg = std::format(fmt, std::forward<Args>(args)...);
std::string_view level_str;
std::string_view color;
switch (level) {
case Level::INFO:
color = INFO_ANSI;
level_str = "[INFO]";
break;
case Level::ERROR:
color = ERROR_ANSI;
level_str = "[ERROR]";
break;
case Level::WARN:
color = WARN_ANSI;
level_str = "[WARN]";
break;
case Level::DEBUG:
color = DEBUG_ANSI;
level_str = "[DEBUG]";
break;
}
std::string message =
std::format("{}[{:%Y-%m-%d %H:%M:%S}]{}", level_str, now_time, msg);
if (m_file_write) {
std::shared_lock lock{m_mutex};
auto today = std::chrono::floor<std::chrono::days>(
std::chrono::system_clock::now());
auto date = std::chrono::year_month_day{today};
if (!m_today.ok() || date > m_today) {
lock.unlock();
std::unique_lock l(m_mutex);
auto today = std::chrono::floor<std::chrono::days>(
std::chrono::system_clock::now());
auto date = std::chrono::year_month_day{today};
if (!m_today.ok() || date > m_today) {
m_today = date;
try {
std::filesystem::create_directories(m_logs_path);
m_file_stream->close();
std::filesystem::path log =
m_logs_path / std::format("{:%Y-%m-%d}.log", date);
m_file_stream->open(log, std::ios::app);
if (!m_file_stream->is_open()) {
std::osyncstream(std::cerr)
<< "File Stream Open Error\n";
}
// direct write
if (m_file_stream && m_file_stream->is_open()) {
std::osyncstream sync_out{*m_file_stream};
std::println(sync_out, "{}", message);
}
} catch (const std::exception& e) {
std::osyncstream(std::cerr)
<< "Catch Logger Error " << e.what() << "\n";
}
}
} else {
if (m_file_stream && m_file_stream->is_open()) {
std::osyncstream sync_out{*m_file_stream};
std::println(sync_out, "{}", message);
}
}
}
if (m_console_write) {
std::osyncstream sync_out(level == Level::ERROR ? std::cerr
: std::cout);
std::println(sync_out, "{}{}{}", color, message, CLEAR_ANSI);
}
}
private:
static constexpr std::string_view CLEAR_ANSI = "\033[0m";
static constexpr std::string_view INFO_ANSI = "\033[1;32m";
static constexpr std::string_view ERROR_ANSI = "\033[1;31m";
static constexpr std::string_view DEBUG_ANSI = "\033[1;34m";
static constexpr std::string_view WARN_ANSI = "\033[1;33m";
std::filesystem::path m_logs_path{"logs"};
std::shared_mutex m_mutex;
std::atomic<bool> m_file_write{false};
std::atomic<bool> m_console_write{true};
std::atomic<Level> m_level{Level::INFO};
std::chrono::year_month_day m_today{};
std::unique_ptr<std::ofstream> m_file_stream;
};
} // namespace Cubed

View File

@@ -0,0 +1,78 @@
#pragma once
#include <asio/error.hpp>
#include <string_view>
namespace Cubed {
inline std::string_view net_error_message(const asio::error_code& ec) {
if (!ec)
return "No error.";
#define NET_CASE(err, msg) \
if (ec == asio::error::err) { \
return msg; \
}
// ===== basic_errors =====
NET_CASE(access_denied, "Access denied.")
NET_CASE(address_family_not_supported, "Address family is not supported.")
NET_CASE(address_in_use, "Address is already in use.")
NET_CASE(already_connected, "Already connected.")
NET_CASE(already_started, "Operation already in progress.")
NET_CASE(broken_pipe, "Broken pipe.")
NET_CASE(connection_aborted, "Connection aborted.")
NET_CASE(connection_refused, "Connection refused by the remote host.")
NET_CASE(connection_reset, "Connection reset by the remote host.")
NET_CASE(bad_descriptor, "Invalid socket or file descriptor.")
NET_CASE(fault, "Bad memory address.")
NET_CASE(host_unreachable, "Host is unreachable.")
NET_CASE(in_progress, "Operation is in progress.")
NET_CASE(interrupted, "Operation interrupted.")
NET_CASE(invalid_argument, "Invalid argument.")
NET_CASE(message_size, "Message is too large.")
NET_CASE(name_too_long, "Name is too long.")
NET_CASE(network_down, "Network is down.")
NET_CASE(network_reset, "Network connection was reset.")
NET_CASE(network_unreachable, "Network is unreachable.")
NET_CASE(no_descriptors, "Too many open sockets or files.")
NET_CASE(no_buffer_space, "No buffer space available.")
NET_CASE(no_memory, "Out of memory.")
NET_CASE(no_permission, "Operation not permitted.")
NET_CASE(no_protocol_option, "Protocol option is unavailable.")
NET_CASE(no_such_device, "No such device.")
NET_CASE(not_connected, "Socket is not connected.")
NET_CASE(not_socket, "Object is not a socket.")
NET_CASE(operation_aborted, "Operation cancelled.")
NET_CASE(operation_not_supported, "Operation is not supported.")
NET_CASE(shut_down, "Connection has been shut down.")
NET_CASE(timed_out, "Operation timed out.")
NET_CASE(try_again, "Temporary failure. Please try again.")
NET_CASE(would_block, "Operation would block.")
// ===== netdb_errors =====
NET_CASE(host_not_found, "Host could not be resolved.")
NET_CASE(host_not_found_try_again, "Temporary DNS resolution failure.")
NET_CASE(no_data, "No address data available.")
NET_CASE(no_recovery, "Non-recoverable DNS error.")
// ===== addrinfo_errors =====
NET_CASE(service_not_found, "Requested service was not found.")
NET_CASE(socket_type_not_supported, "Socket type is not supported.")
// ===== misc_errors =====
NET_CASE(already_open, "Resource is already open.")
NET_CASE(eof, "Connection closed by the remote host.")
NET_CASE(not_found, "Requested item was not found.")
NET_CASE(fd_set_failure, "Too many descriptors for select().")
#undef NET_CASE
return "Unknown network error.";
}
} // namespace Cubed

View File

@@ -0,0 +1,24 @@
#pragma once
#include <nlohmann/json.hpp>
namespace Cubed {
class SensitiveFilter {
public:
SensitiveFilter();
void load(const nlohmann::json& j);
std::string filter(std::string_view text);
private:
void insert(const std::u32string& world);
void build();
struct Node {
std::unordered_map<char32_t, int> next;
int fail = 0;
size_t length = 0;
bool end = false;
};
std::vector<Node> trie{1};
};
} // namespace Cubed

View File

@@ -1,11 +1,43 @@
#pragma once
#include <SOIL2.h>
#include <glad/glad.h>
#include <string>
#include <utility>
namespace Cubed {
namespace Tools {
void delete_image_data(unsigned char* data);
}
struct ImageData {
unsigned char* data = nullptr;
int width = 0;
int height = 0;
int channels = 0;
ImageData(const ImageData&) = delete;
ImageData(ImageData&& o) noexcept
: data(std::exchange(o.data, nullptr)), width(o.width),
height(o.height), channels(o.channels) {}
ImageData& operator=(const ImageData&) = delete;
ImageData& operator=(ImageData&& o) noexcept {
if (this == &o) {
return *this;
}
if (data) {
Tools::delete_image_data(data);
}
data = std::exchange(o.data, nullptr);
width = o.width;
height = o.height;
channels = o.channels;
return *this;
}
ImageData(unsigned char* d, int w, int h, int c)
: data(d), width(w), height(h), channels(c) {}
ImageData() = default;
~ImageData() { Tools::delete_image_data(data); }
};
namespace Tools {
GLuint create_shader_program(const std::string& v_shader_path,
const std::string& f_shader_path);
@@ -13,9 +45,9 @@ void print_shader_log(GLuint shader);
void print_program_info(int prog);
bool check_opengl_error();
std::string read_shader_source(const std::string& file_path);
void delete_image_data(unsigned char* data);
unsigned char* load_image_data(const std::string& tex_image_path,
bool check_exist = true);
ImageData load_image_data(const std::string& tex_image_path,
bool check_exist = true);
} // namespace Tools

View File

@@ -1,13 +1,17 @@
#pragma once
#include "Cubed/tools/log.hpp"
#include "Cubed/tools/log.hpp" // IWYU pragma: keep
#include <string>
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
// clang-format off
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <windows.h>
#undef DELETE
#include <psapi.h>
// clang-format on
typedef LONG(WINAPI* RtlGetVersionPtr)(PRTL_OSVERSIONINFOW);
@@ -153,6 +157,46 @@ inline std::string get_cpu_info() {
#endif
}
inline std::string get_compiler_info() {
std::string result;
#if defined(__clang__)
result = "Clang ";
result += __clang_version__;
#elif defined(__GNUC__)
result = "GCC ";
result += std::to_string(__GNUC__);
result += ".";
result += std::to_string(__GNUC_MINOR__);
result += ".";
result += std::to_string(__GNUC_PATCHLEVEL__);
#elif defined(_MSC_VER)
int major = _MSC_VER / 100;
int minor = _MSC_VER % 100;
result = "MSVC ";
result += std::to_string(major);
result += ".";
result += std::to_string(minor);
#else
result = "Unknown Compiler";
#endif
constexpr long CPP_VERSION =
#ifdef _MSC_VER
_MSVC_LANG;
#else
__cplusplus;
#endif
result += " (C++";
result += std::to_string(CPP_VERSION);
result += ")";
return result;
}
} // namespace Tools
} // namespace Cubed

View File

@@ -0,0 +1,13 @@
#pragma once
#include <string>
namespace Cubed {
struct SystemLocale {
std::string country; // CN、US、JP...
std::string locale; // zh_CN、en_US...
};
SystemLocale get_system_locale();
} // namespace Cubed

View File

@@ -0,0 +1,28 @@
#pragma once
#include "Cubed/tools/log.hpp"
#include <cstdlib>
#include <string>
namespace Cubed {
namespace Tools {
inline std::string detect_wm() {
#ifdef _WIN32
return "Windows";
#endif
#ifdef __linux__
const char* desktop = getenv("XDG_CURRENT_DESKTOP");
if (!desktop) {
return "Unknown";
}
Logger::info("XDG_CURRENT_DESKTOP: {}", desktop);
return std::string(desktop);
#endif
return "Unknown";
}
} // namespace Tools
} // namespace Cubed

View File

@@ -0,0 +1,69 @@
#pragma once
#include "Cubed/tools/cubed_assert.hpp"
#include <cstdint>
#include <string>
#include <utf8cpp/utf8.h>
#include <vector>
namespace Cubed {
inline std::string codepoint_to_utf8(uint32_t cp) {
std::string out;
if (cp <= 0x7F) {
out.push_back(static_cast<char>(cp));
} else if (cp <= 0x7FF) {
out.push_back(static_cast<char>(0xC0 | (cp >> 6)));
out.push_back(static_cast<char>(0x80 | (cp & 0x3F)));
} else if (cp <= 0xFFFF) {
out.push_back(static_cast<char>(0xE0 | (cp >> 12)));
out.push_back(static_cast<char>(0x80 | ((cp >> 6) & 0x3F)));
out.push_back(static_cast<char>(0x80 | (cp & 0x3F)));
} else {
out.push_back(static_cast<char>(0xF0 | (cp >> 18)));
out.push_back(static_cast<char>(0x80 | ((cp >> 12) & 0x3F)));
out.push_back(static_cast<char>(0x80 | ((cp >> 6) & 0x3F)));
out.push_back(static_cast<char>(0x80 | (cp & 0x3F)));
}
return out;
}
inline void utf8_pop_back(std::string& str) {
if (str.empty()) {
return;
}
std::size_t i = str.size() - 1;
while (i > 0 && (static_cast<unsigned char>(str[i]) & 0xC0) == 0x80) {
--i;
}
str.erase(i);
}
inline std::vector<std::string> split_utf8(const std::string& str, int size) {
ASSERT(size > 0);
if (!utf8::is_valid(str)) {
return {};
}
std::vector<std::string> blocks;
auto cur = str.begin();
int chat_count = 0;
for (auto it = str.begin(); it != str.end();) {
if (chat_count >= size) {
blocks.emplace_back(cur, it);
cur = it;
chat_count = 0;
}
utf8::next(it, str.end());
++chat_count;
if (it == str.end()) {
blocks.emplace_back(cur, it);
}
}
return blocks;
}
} // namespace Cubed

View File

@@ -0,0 +1,10 @@
#pragma once
#include <SDL3/SDL_timer.h>
#include <cstdint>
namespace Cubed {
namespace Tools {
inline uint64_t get_time_ticks() { return SDL_GetTicks(); }
} // namespace Tools
} // namespace Cubed

View File

@@ -29,6 +29,12 @@ std::optional<T> safe_get_value(const toml::table& table, std::string_view key,
return value;
}
inline std::optional<std::string>
safe_get_value(const toml::table& table, std::string_view key,
std::string_view default_value) {
return safe_get_value(table, key, std::string(default_value));
}
} // namespace TOML
} // namespace Cubed

Some files were not shown because too many files have changed in this diff Show More