Compare commits

...

140 Commits

Author SHA1 Message Date
28f6cb8f97
Readd jmalloc to vcpkg.json 2024-09-09 18:55:05 +03:00
ef307a5492
Merge branch 'main' of https://github.com/OpenStarbound/OpenStarbound 2024-09-09 18:40:47 +03:00
Kae
8457c2e954
Merge pull request #110 from Lonaasan/main
[Small Addition] Added respawnInWorld Command
2024-09-09 20:52:06 +10:00
Kae
f59f6be5d3 oop 2024-09-09 20:35:08 +10:00
Kae
95b13f670d code clean-up 2024-09-09 20:32:23 +10:00
lonaasan
227585f1dc [bugfix] removed check in setRespawnWorld due to the nature of NULL 2024-09-09 11:35:40 +02:00
lonaasan
732fc2a9d7 [Revision] Applying the recommended changes from pull request #110 (return current value if no argument given, moving the methods to their correct location) 2024-09-09 11:27:14 +02:00
lonaasan
98a395721e [Small Addition] added respawnInWorld Command 2024-09-09 09:53:11 +02:00
Kae
06b865fb84
Merge pull request #109 from floydinator-git/main
add assets.exists
2024-09-07 14:35:17 +10:00
floyd
fc1b97c4bc
Update StarAssets.cpp 2024-09-07 00:03:07 -04:00
Kae
6133d5beb1
Merge pull request #108 from Lonaasan/main
[Small Addition] Added custom OpenSB help text categories and descriptions
2024-09-07 13:27:21 +10:00
lonaasan
57e9d13e6a Added custom OpenSB help text categories and descriptions 2024-09-06 23:29:02 +02:00
Kae
90db1e0fba add Player::currentState 2024-09-02 22:18:09 +10:00
Kae
efa57d3081 UniverseClient: log packet type when a packet throws an exception 2024-09-02 22:18:03 +10:00
Kae
ac7577b4df Rename Keypad enums
noticed keypad binds were named like this in SE (probably due to using the names given by SDL there) and it's nicer anyway. better do this sooner than later.
2024-09-02 22:17:26 +10:00
Kae
566b0f4d36
make barstound rarer 2024-08-28 13:04:08 +10:00
Kae
3c9c65fa38 fix sapling infinite loop 2024-08-25 20:29:22 +10:00
Kae
e3462c3c69 Update StarUniverseServer.cpp 2024-08-25 20:28:50 +10:00
Kae
f0a08aea22 win: console if launching with debugger 2024-08-25 20:28:42 +10:00
Kae
8332536399 fix compilation 2024-08-22 12:52:59 +10:00
Kae
4496cc17af
Merge pull request #101 from Bottinator22/main
Post-processing layer support for mods
2024-08-22 12:43:12 +10:00
Bottinator22
474fad6534
Update client.config.patch 2024-08-21 19:25:41 -07:00
Bottinator22
ab48b22b96
Add files via upload 2024-08-21 19:24:55 -07:00
Bottinator22
08dd7d72d7
Update StarClientApplication.hpp 2024-08-21 19:23:11 -07:00
Bottinator22
b0d3c88834
Update StarClientApplication.cpp 2024-08-21 19:22:25 -07:00
Bottinator22
8937f0d816
Update StarRenderer_opengl.hpp 2024-08-21 19:21:50 -07:00
Bottinator22
50c987b1c4
Update StarRenderer_opengl.cpp 2024-08-21 19:21:02 -07:00
Kae
483908abef
Merge pull request #100 from OpenStarbound/SilverSokolova-patch-1
Elaborate on dungeon air vs ground error message
2024-08-21 17:23:14 +10:00
Kae
25386deef4
Use formatting for that exception msg 2024-08-21 17:20:49 +10:00
SilverSokolova
d9ca18198e
Elaborate on dungeon air vs ground error message 2024-08-21 00:21:51 -05:00
Kae
b2f8ac594a Update StarPortraitWidget.cpp 2024-08-20 19:58:20 +10:00
Kae
76a00ac5c9 Update StarInventory.cpp 2024-08-18 14:15:07 +10:00
Kae
d7065e7611 Update StarThread_unix.cpp 2024-08-14 16:51:58 +10:00
Kae
295ed51126 Update StarThread_unix.cpp
my brain is kind of fried at the moment
2024-08-14 13:45:17 +10:00
Kae
86a1de4fbd Update StarThread_unix.cpp 2024-08-14 13:30:44 +10:00
Kae
10c4cff2ed Update StarThread_unix.cpp 2024-08-14 13:26:40 +10:00
Kae
cadd5b32ff Merge branch 'main' of https://github.com/OpenStarbound/OpenStarbound 2024-08-14 11:06:26 +10:00
Kae
1a5507f019 Update StarClientApplication.hpp 2024-08-14 11:06:23 +10:00
SilverSokolova
33e5476ab3
player.facialMask and player.facialHair stuff 2024-08-13 19:47:45 -05:00
Kae
8bef4abdbc Connection changes 2024-08-13 16:23:01 +10:00
SilverSokolova
b39072f071
Update openstarbound.md 2024-08-11 13:25:54 -05:00
Kae
4e236740f6 Update StarLogging.cpp 2024-08-08 13:06:42 +10:00
Kae
ffc1f95789 nicer error logging for BTree Repacker
recoverAll was really just sitting there
2024-08-08 12:09:47 +10:00
Kae
b2afd65144 Fix WorldStorage accessing null entityMap if a broken world throws an exception on load
happened to me when trying to repair a broken world file
2024-08-08 11:59:38 +10:00
Kae
dc37c9bdb8 Check if object matspace is a biome metamaterial when removing old matspaces
#95
2024-08-05 10:07:22 +10:00
Kae
cb547d0bc6 Improve Windows stack output 2024-08-04 21:22:50 +10:00
Kae
9b3bf5ae82
Merge pull request #94 from RohanBhattacharyya/main
Fix LeftStick movement StarClientApplication.cpp
2024-08-04 08:33:35 +10:00
Rohan
8f0c327ffa
Fix LeftStick movement StarClientApplication.cpp
It no longer has that drift. The drift is caused by the controller, and this simply turns up the threshold of where the game actually accepts the input as a movement.
2024-08-03 11:42:20 -07:00
Kae
6321a7d75d Update StarClientApplication.cpp 2024-08-03 18:54:49 +10:00
Kae
20de634a06 Improve Discord activity info
requested by Omeruin!
2024-08-03 18:51:19 +10:00
Kae
bef86811c9 revert libsamplerate addition
sadge
2024-08-03 13:36:16 +10:00
Kae
4f511c2aaa Update build_linux.yml 2024-08-03 13:32:41 +10:00
Kae
908fa1ee60 Add libsamplerate, make Voice bitrate configurable 2024-08-03 11:54:08 +10:00
Kae
497c6efc55 Fix RNG bugs from upgrade to C++17
staticRandomShuffle now uses its own tiny impl of the deprecated std::random_shuffle, producing identical results in testing
2024-08-02 11:53:59 +10:00
Kae
53c4dc3491 Merge branch 'main' of https://github.com/OpenStarbound/OpenStarbound 2024-07-31 08:41:00 +10:00
Kae
42acfb3f2d fix F16-F24 keys offsetting keycodes, add key name to canvas key callback args 2024-07-31 08:40:56 +10:00
SilverSokolova
6e7b046796
player.(set)favoriteColor documentation 2024-07-30 03:24:08 -05:00
Kae
420f1d9731 add player.favoriteColor and setFavoriteColor 2024-07-30 14:00:03 +10:00
Kae
a75703085e Ignore GLEW wayland error (https://github.com/nigels-com/glew/issues/172) 2024-07-30 12:41:24 +10:00
Kae
0990379db8 Update StarRenderer_opengl.cpp 2024-07-30 12:25:01 +10:00
Kae
db836d0ca4 Update StarRenderer_opengl.cpp 2024-07-30 12:19:18 +10:00
Kae
a5f42ce40a improve linux server compatibility 2024-07-30 12:13:43 +10:00
Kae
1224213cab Change the Songbook search field to be more compatible with UI mods
It's at the top of the list now. Closes #90
2024-07-29 14:59:52 +10:00
Kae
e9e87a1c3c Avoid crashing when a OGG file is broken (thanks to @kblaschke !)
Also added a name tag to Audio for logging so that it's easier to find the audio asset that's causing it
2024-07-29 09:23:27 +10:00
Kae
8b1a2d6f0c add 2 new allocators, currently using rpmalloc on Windows (mimalloc unused for now) 2024-07-28 10:54:26 +10:00
Kae
5398190030 relax sector unload criteria 2024-07-28 10:53:14 +10:00
Kae
e1be2ab429 finalize network improvements
can now disable zstd stream compression via config, peer-to-peer uses stream compression now, also made the server commands script a bit nicer
2024-07-27 20:04:34 +10:00
Kae
372921abde Update StarNetPacketSocket.cpp
oops
2024-07-27 19:42:24 +10:00
Kae
0ec199b3af Update StarNetPacketSocket.cpp 2024-07-27 18:56:16 +10:00
Kae
a5788e7585 Update StarNetPackets.cpp 2024-07-27 14:52:36 +10:00
Kae
951fe787c4 Networking changes (needs P2P testing, requires clients to update unfortunately) 2024-07-27 14:09:12 +10:00
Kae
9e7a2e9bb9 only print Lua command result if it's not null 2024-07-27 13:58:14 +10:00
Kae
f95fbc3a37 Update multiplayer.config.patch 2024-07-27 11:24:10 +10:00
Kae
b1315d1784 Update StarPlayerInventory.cpp 2024-07-27 10:43:58 +10:00
Kae
c3de15c18d Fix compressed buffer not being emptied instantly in TcpPacketSocket::writeData
would only cause an issue when using sendAll, resulted in rarely not connecting
2024-07-27 08:43:32 +10:00
Kae
2d278e71c1 fix possible segfault in PlayerInventory::retrieve 2024-07-26 14:54:34 +10:00
Kae
a6ac154b94 accept 🎮 in mod bindings 2024-07-25 08:56:00 +10:00
Kae
323364f0af well that doesn't compile outside MSVC apparently 2024-07-25 07:25:27 +10:00
Kae
0638127721 Update StarObject.cpp 2024-07-25 07:21:17 +10:00
Kae
0634ae78fc Object::init - avoid throwing if 'scripts' parameter isn't an array for some reason 2024-07-25 07:04:21 +10:00
Kae
6dd49fb69a
make missing packed.pak instructions slightly more verbose
ffs. TODO: prompt automatic copy if it detects an existing install
2024-07-24 18:18:52 +10:00
Kae
f46d796253 Propagate client admin status to WorldServers 2024-07-20 05:35:06 +10:00
Kae
42c8933f55
Merge pull request #86 from OpenStarbound/SilverSokolova-patch-1
postload: upscale all HD cursors
2024-07-20 05:17:47 +10:00
Kae
369dcb63d1
disable left-stick movement for now
#87
2024-07-18 06:55:38 +10:00
SilverSokolova
74ea816f8a
postload: upscale all HD cursors 2024-07-17 03:26:32 -05:00
SilverSokolova
044d6a38d4
Delete assets/opensb/cursors/inspect.cursor.patch 2024-07-17 03:25:23 -05:00
SilverSokolova
686dd786ef
Delete assets/opensb/cursors/link.cursor.patch 2024-07-17 03:25:17 -05:00
SilverSokolova
2d6a9d9f7e
Delete assets/opensb/cursors/pointer.cursor.patch 2024-07-17 03:24:22 -05:00
Kae
d319f1b2a2
Merge pull request #85 from OpenStarbound/SilverSokolova-patch-1
Update README.md
2024-07-15 03:41:44 +10:00
SilverSokolova
ff5fa1f70f
Update README.md 2024-07-13 23:29:51 -05:00
Kae
d4fad6402f add F16-F24 keys 2024-07-12 08:09:06 +10:00
Kae
2751daf29f
Merge pull request #83 from OpenStarbound/SilverSokolova-patch-1
Update README.md
2024-07-12 05:26:51 +10:00
Kae
11cc5a71ee
Merge pull request #84 from OpenStarbound/SilverSokolova-patch-2
oSB Lua documentation: root & player
2024-07-11 02:55:53 +10:00
SilverSokolova
661540efe2
Update openstarbound.md 2024-07-09 22:14:50 -05:00
SilverSokolova
dd7e3c91b0
Update openstarbound.md 2024-07-09 22:12:14 -05:00
SilverSokolova
7705394a1c
oSB Lua documentation: root, player 2024-07-09 22:09:41 -05:00
SilverSokolova
c62bf829af
Update README.md
Add PGI item recovery, musical instrument volume, and inventory updating to feature list
2024-07-09 19:19:19 -05:00
Kae
aa612c9378
fix Windows Installer link
[skip ci]
2024-07-10 01:16:49 +10:00
Kae
ca2199c7e3
Merge pull request #79 from RohanBhattacharyya/main
MacOS Build Instructions added to README
2024-07-09 08:58:00 +10:00
Rohan
0f64067683
Update README.md 2024-07-06 19:21:29 -07:00
Rohan
e490fd3a4d
Update README.md 2024-07-06 19:20:07 -07:00
Rohan
6aef6a719c
Update README.md 2024-07-06 19:13:35 -07:00
Rohan
5f9e2338e4
Update README.md 2024-07-06 19:09:31 -07:00
Rohan
7123d7a3a7
Update README.md 2024-07-06 19:05:23 -07:00
Rohan
457e6805b6
Update README.md 2024-07-06 17:45:48 -07:00
Rohan
642b1b1426
Update README.md 2024-07-06 17:39:03 -07:00
Rohan
7fa270a3a7
Update README.md 2024-07-06 17:34:11 -07:00
Rohan
03a6d4e8ed
Update README.md 2024-07-06 17:32:31 -07:00
Rohan
aff00139ff
Update README.md 2024-07-06 17:30:19 -07:00
Rohan
36e8d29ad4
Update README.md 2024-07-06 17:29:04 -07:00
Rohan
b2b56c7967
Update README.md 2024-07-06 17:25:30 -07:00
Rohan
b7ff17bb33
Update README.md 2024-07-06 17:24:44 -07:00
Rohan
e1e3246b26
Update README.md 2024-07-06 17:23:58 -07:00
Kae
bc2e805d01 Bump shader versions
#78
2024-07-07 07:32:06 +10:00
Kae
d313a3ceb3 OpenGL: use 4.1 core
necessary for modern Mac support
2024-07-07 04:44:01 +10:00
Kae
4b0d1cb90d
Merge pull request #76 from Kilkenni/player-bindings-return-values
return values for bookmark Lua callbacks
2024-07-02 22:37:55 +10:00
Kae
49f84c7a3b add missing return statement 2024-07-02 21:26:30 +10:00
Kilkenni
0acce4b871 return values for bookmark Lua callbacks 2024-07-01 19:18:40 +03:00
Kae
54ac208dd5 lighting: disable the new additive point light behavior when new lighting is off 2024-06-28 17:10:17 +10:00
Kae
bb5387fbdb fix seemingly extremely rare race condition leading to exception when joining a singleplayer internal server
happened when the client thread called universeServer->setPause (which locks m_clientsLock) between the client id being added to m_clients and the connection being added to m_connectionServer
2024-06-28 17:09:13 +10:00
Kae
4120a289db update fmtlib 2024-06-27 23:39:48 +10:00
Kae
f60a19e065 optional sbinit option to disable UGC (workshop mods) 2024-06-27 15:49:41 +10:00
Kae
64adc28658 fix the very last material color variant not displaying 2024-06-27 14:29:43 +10:00
Kae
c90e738730
Update README.md
[skip ci]
2024-06-26 17:37:30 +10:00
LDA
e1abce7091 allow compiling with old versions of sdl2 2024-06-25 22:13:51 -07:00
Kae
624d41f94a Fix MaterialItem not entirely uninitializing
was causing an exception when a MaterialItem had a script on it
2024-06-26 13:04:19 +10:00
Kae
563d95b9e6 remove redundant vertexRounding uniform from interface shader
interface is always drawn without super-sampling anyway
2024-06-26 13:03:30 +10:00
Kae
c405fda45c Update StarAssets.cpp 2024-06-25 20:03:35 +10:00
Kae
9edbe8cf2d Add .patchlist
#73
2024-06-25 19:56:44 +10:00
Kae
67c7257c3b Update StarCharSelection.cpp 2024-06-25 19:56:19 +10:00
Kae
c046bd83d1
Merge pull request #71 from OpenStarbound/SilverSokolova-patch-1
make `/help run` tell you about the run command
2024-06-24 18:56:04 +10:00
Kae
ce032d8e0c Include steam_appid.txt by default 2024-06-24 18:35:35 +10:00
Kae
e1b1b2fd59 Ensure the chunk & system that the player's ship is always in their local chunk cache
#74
2024-06-24 14:08:04 +10:00
Kae
4c90472977 Read object script paths from params again taking relative paths into account
also made build artifact names a bit more consistent
2024-06-22 14:02:02 +10:00
Kae
83686a816c
revert Object script change for now
didn't consider relative paths
2024-06-20 09:16:37 +10:00
Kae
39a6e900a4 Inspecting now logs to the chat
TODO: make configurable!
2024-06-17 20:31:40 +10:00
Kae
f7d2303fe0 add Object::clientEntityMode, & read scripts from params
Suggested by Bott
2024-06-17 20:22:26 +10:00
LDA
6ded71d9eb tests can link again AND THEY PASS!!! 2024-06-16 09:28:28 -07:00
SilverSokolova
6654e4da27
make /help run tell you about the run command 2024-06-11 09:41:46 -05:00
Kae
fc50600303 add Projectile::velocity
thanks to Bott for suggesting
2024-06-09 15:25:23 +10:00
131 changed files with 7843 additions and 1957 deletions

View File

@ -51,7 +51,7 @@ jobs:
- name: Upload Artifacts - name: Upload Artifacts
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: OpenStarbound-Dev-macOS-Intel name: OpenStarbound-macOS-Intel
path: dist/* path: dist/*
build-arm: build-arm:
@ -93,5 +93,5 @@ jobs:
- name: Upload Artifacts - name: Upload Artifacts
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: OpenStarbound-Dev-macOS-Silicon name: OpenStarbound-macOS-Silicon
path: dist/* path: dist/*

View File

@ -85,5 +85,5 @@ jobs:
- name: Upload Installer - name: Upload Installer
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: Installer name: OpenStarbound-Windows-Installer
path: installer/* path: installer/*

View File

@ -9,13 +9,17 @@ Original README is as follows, if you're here reading it, you can skip the steps
This is a fork of Starbound. Contributions are welcome! This is a fork of Starbound. Contributions are welcome!
You **must** own a copy of Starbound to use it. Base game assets are not provided for obvious reasons. You **must** own a copy of Starbound to use it. Base game assets are not provided for obvious reasons.
It is still **work-in-progress**. You can download the very latest build below, or the occasional releases (though those aren't very up to date yet!) It is still **work-in-progress**.
## Nightly Builds
At the moment, you must copy the game assets (**packed.pak**) from your normal Starbound install to the OpenStarbound assets directory. ## Installation
You can download a nightly build below, or the [latest release](https://github.com/OpenStarbound/OpenStarbound/releases/latest). At the moment, you must copy the game assets (**packed.pak**) from your normal Starbound install to the OpenStarbound assets directory before playing.
An installer is available for Windows. otherwise, extract the client/server zip for your platform and copy the game assets (packed.pak) to the OpenStarbound assets folder. the macOS releases currently lack the sbinit.config and folder structure that the Linux & Windows zips have, so you'll need to create those before running them. For macOS releases, it is recommended to build them from source (See guide below).
### Nightly Builds
These link directly to the latest build from the [Actions](https://github.com/OpenStarbound/OpenStarbound/actions?query=branch%3Amain) tab.
[**Windows**](https://nightly.link/OpenStarbound/OpenStarbound/workflows/build_windows/main): [**Windows**](https://nightly.link/OpenStarbound/OpenStarbound/workflows/build_windows/main):
[Installer](https://nightly.link/OpenStarbound/OpenStarbound/workflows/build_windows/main/Installer.zip), [Installer](https://nightly.link/OpenStarbound/OpenStarbound/workflows/build_windows/main/OpenStarbound-Windows-Installer.zip),
[Client](https://nightly.link/OpenStarbound/OpenStarbound/workflows/build_windows/main/OpenStarbound-Windows-Client.zip), [Client](https://nightly.link/OpenStarbound/OpenStarbound/workflows/build_windows/main/OpenStarbound-Windows-Client.zip),
[Server](https://nightly.link/OpenStarbound/OpenStarbound/workflows/build_windows/main/OpenStarbound-Windows-Server.zip) [Server](https://nightly.link/OpenStarbound/OpenStarbound/workflows/build_windows/main/OpenStarbound-Windows-Server.zip)
@ -27,8 +31,6 @@ At the moment, you must copy the game assets (**packed.pak**) from your normal S
[Intel](https://nightly.link/OpenStarbound/OpenStarbound/workflows/build_macos/main/OpenStarbound-Dev-macOS-Intel.zip), [Intel](https://nightly.link/OpenStarbound/OpenStarbound/workflows/build_macos/main/OpenStarbound-Dev-macOS-Intel.zip),
[ARM](https://nightly.link/OpenStarbound/OpenStarbound/workflows/build_macos/main/OpenStarbound-Dev-macOS-Silicon.zip) [ARM](https://nightly.link/OpenStarbound/OpenStarbound/workflows/build_macos/main/OpenStarbound-Dev-macOS-Silicon.zip)
These link directly to the latest build from the [Actions](https://github.com/OpenStarbound/OpenStarbound/actions?query=branch%3Amain) tab.
## Changes ## Changes
Note: Not every function from [StarExtensions](https://github.com/StarExtensions/StarExtensions) has been ported yet, but near-full compatibility with mods that use StarExtensions features is planned. Note: Not every function from [StarExtensions](https://github.com/StarExtensions/StarExtensions) has been ported yet, but near-full compatibility with mods that use StarExtensions features is planned.
@ -42,8 +44,10 @@ Note: Not every function from [StarExtensions](https://github.com/StarExtensions
* These scripts can modify, read, patch and create new assets! * These scripts can modify, read, patch and create new assets!
* Lua patch files now exist - **.patch.lua** * Lua patch files now exist - **.patch.lua**
* These can patch JSON assets, as well as images! * These can patch JSON assets, as well as images!
### Bug Fixes
* Invalid character inventories are updated when loading in, allowing players to swap inventory mods with pre-existing characters.
### Misc ### Misc
* Player functions for saving/loading, modifying the humanoid identity * Player functions for saving/loading, modifying the humanoid identity, manipulating the inventory. [Documentation](https://github.com/OpenStarbound/OpenStarbound/blob/main/doc/lua/openstarbound.md)
* Character swapping (rewrite from StarExtensions, currently command-only: `/swap name` case-insensitive, only substring required) * Character swapping (rewrite from StarExtensions, currently command-only: `/swap name` case-insensitive, only substring required)
* Custom user input support with a keybindings menu (rewrite from StarExtensions) * Custom user input support with a keybindings menu (rewrite from StarExtensions)
* Positional Voice Chat that works on completely vanilla servers, uses Opus for crisp, HD audio (rewrite from StarExtensions) * Positional Voice Chat that works on completely vanilla servers, uses Opus for crisp, HD audio (rewrite from StarExtensions)
@ -52,11 +56,15 @@ Note: Not every function from [StarExtensions](https://github.com/StarExtensions
* **.woff2** fonts are much smaller than **.ttf**, [here's a web conversion tool](https://kombu.kanejaku.org/)! * **.woff2** fonts are much smaller than **.ttf**, [here's a web conversion tool](https://kombu.kanejaku.org/)!
* Experimental changes to the storage of directives in memory to reduce copying - can reduce their impact on frametimes when very long directives are present * Experimental changes to the storage of directives in memory to reduce copying - can reduce their impact on frametimes when very long directives are present
* Works especially well when extremely long directives are used for "vanilla multiplayer-compatible" creations, like [generated clothing](https://silverfeelin.github.io/Starbound-NgOutfitGenerator/) or custom items/objects. * Works especially well when extremely long directives are used for "vanilla multiplayer-compatible" creations, like [generated clothing](https://silverfeelin.github.io/Starbound-NgOutfitGenerator/) or custom items/objects.
* Perfectly Generic Items will retain the data for what item they were if a mod is uninstalled, and will attempt to restore themselves if re-installed.
* Musical instruments have their own volume slider in the options menu.
* Players can use items while lounging
* Client-side tile placement prediction (rewrite from StarExtensions) * Client-side tile placement prediction (rewrite from StarExtensions)
* You can also resize the placement area of tiles on the fly. * You can also resize the placement area of tiles on the fly.
* Support for placing foreground tiles with a custom collision type (rewrite from StarExtensions, requires OpenSB server) * Support for placing foreground tiles with a custom collision type (rewrite from StarExtensions, requires OpenSB server)
* Additionally, objects can be placed under non-solid foreground tiles. * Additionally, objects can be placed under non-solid foreground tiles.
* Admin characters have unlimited and unobstructed interaction/placement ranges
* Some minor polish to UI * Some minor polish to UI
* The Skybox's sun now matches the system type you're currently in. * The Skybox's sun now matches the system type you're currently in.
@ -203,6 +211,44 @@ LD_LIBRARY_PATH="$LD_LIBRARY_PATH:./" padsp ./starbound "$@"`
</details> </details>
</details> </details>
<details>
<summary><b>macOS</b></summary>
### macOS * First, you will need to have brew installed. Check out how to install [Homebrew](https://brew.sh/)
To be written. * Install cmake using `brew install cmake`
* Install ninja using `brew install ninja`
* Install pkg config using `brew install pkg-config`
* Next, install vcpkg by following the commands below.
* Run `cd ~`. This is just so that everything is local to here.
* Run ` git clone https://github.com/microsoft/vcpkg.git `
* Run `cd vcpkg && ./bootstrap-vcpkg.sh`
* Lastly, run ``` export VCPKG_ROOT=~/vcpkg && export PATH=$VCPKG_ROOT:$PATH ```
* This last command makes vcpkg added to the current terminal path. This lasts only while the terminal is active, and will have to be rerun for new terminal instances.
* Download the source code [here](https://github.com/OpenStarbound/OpenStarbound/archive/refs/heads/main.zip). This is the current code in main. Unpack the code to your downloads folder.
* Unpack the zip, and open it up. Navigate to OpenStarbound-main/source using the terminal -> `cd ~/Downloads/OpenStarbound-main`. Then navigate to the source folder, using `cd source`.
<details>
<summary>If using an Arm Mac</summary>
* While in the source folder in your terminal, run ` cmake --preset macos-arm-release `. This will get dependencies.
* After that command has finished, run ` cmake --build --preset macos-arm-release `. Wait for this to finish, then go to Finder. Navigate to the OpenStarbound-main folder using Finder.
* There will be a folder called <b>dist</b>. Inside dist will be your game files, but you still need to do a few more things to run it.
* First, in the OpenStarbound-main folder, there will be lib. Open lib, and open the osx folder. Inside is libsteam_api.dylib. Copy this file, and paste it into OpenStarbound-main/dist, so that it is in the same directory as the game files.
* Navigate back to OpenStarbound-main/lib/osx, and open up the folder arm64. Here, rename libdiscord_game_sdk.dylib to discord_game_sdk.dylib. The name must be that, or else the game won't be able to load.
* Grab the packed.pak file from your current Starbound install. It will be located in the assets folder. Copy that file into OpenStarbound-main/assets.
* Make a new file called sbinit.config (Make sure it is .config, not .somethingelse), and copy and paste in the sbinit.config text from above, located right underneath the title Building. Place sbinit.config inside OpenStarbound-main/dist. To make a new file, open the program called TextEdit on your mac, paste in the sbinit.config text from above, and click File (located at the very top of your screen), then click Save. It will prompt you, asking where to save it. Save As: sbinit.config, Where: Navigate to OpenStarbound-main/dist. Find the file you just saved, and rename it to get rid of the wrong extension, making sure the full name and extension looks like sbinit.config.
* You can now run the game by double clicking on the file called starbound in dist/. If it says unverified developer, open up the same folder where the game is in in the terminal. ` xattr -d com.apple.quarantine starbound `, which will get rid of the lock on the file. If that doesn't work, run ` sudo spctl --master-disable ` to allow all unverified apps.
</details>
<details>
<summary>If using an Intel Mac</summary>
* While in the source folder in your terminal, run ` cmake --preset macos-release `. This will get dependencies.
* After that command has finished, run ` cmake --build --preset macos-release `. Wait for this to finish, then go to Finder. Navigate to the OpenStarbound-main folder using Finder.
* There will be a folder called <b>dist</b>. Inside dist will be your game files, but you still need to do a few more things to run it.
* First, in the OpenStarbound-main folder, there will be lib. Open lib, and open the osx folder. Inside is libsteam_api.dylib. Copy this file, and paste it into OpenStarbound-main/dist, so that it is in the same directory as the game files.
* Navigate back to OpenStarbound-main/lib/osx, and open up the folder x64. Here, rename libdiscord_game_sdk.dylib to discord_game_sdk.dylib. The name must be that, or else the game won't be able to load.
* Grab the packed.pak file from your current Starbound install. It will be located in the assets folder. Copy that file into OpenStarbound-main/assets.
* Make a new file called sbinit.config (Make sure it is .config, not .somethingelse), and copy and paste in the sbinit.config text from above, located right underneath the title Building. Place sbinit.config inside OpenStarbound-main/dist. To make a new file, open the program called TextEdit on your mac, paste in the sbinit.config text from above, and click File (located at the very top of your screen), then click Save. It will prompt you, asking where to save it. Save As: sbinit.config, Where: Navigate to OpenStarbound-main/dist. Find the file you just saved, and rename it to get rid of the wrong extension, making sure the full name and extension looks like sbinit.config.
* You can now run the game by double clicking on the file called starbound in dist/. If it says unverified developer, open up the same folder where the game is in in the terminal. ` xattr -d com.apple.quarantine starbound `, which will get rid of the lock on the file. If that doesn't work, run ` sudo spctl --master-disable ` to allow all unverified apps.
</details>
</details>

View File

@ -9,5 +9,6 @@
"deployCinematicBase" : { "deployCinematicBase" : {
"scissor" : false, "scissor" : false,
"letterbox" : false "letterbox" : false
} },
"postProcessLayers": []
} }

View File

@ -1,3 +0,0 @@
{
"scale" : 1
}

View File

@ -1,3 +0,0 @@
{
"scale" : 1
}

View File

@ -1,3 +0,0 @@
{
"scale" : 1
}

View File

@ -1,5 +1,16 @@
{ {
"basicHelpText" : "Basic commands are: {}", "basicHelpText": "Basic commands are: {}",
"adminHelpText" : "Admin commands are: {}", "adminHelpText": "Admin commands are: {}",
"debugHelpText" : "Debug commands are: {}" "debugHelpText": "Debug commands are: {}",
"openSbHelpText": "OpenSB commands are: {}",
"openSbDebugHelpText": "OpenSB Debug commands are: {}",
"openSbDebugCommands": {
"run": "Usage /run <lua>. Executes a script on the player and outputs the return value to chat."
},
"openSbCommands": {
"swap": "Usage /swap <name>. Swaps the current character, case-insensitive, only substring required.",
"respawninworld": "Usage /respawninworld. Sets the respawn flag for the current world until you teleport away."
}
} }

View File

@ -4,7 +4,7 @@ function patch(original)
image:copyInto({0, 0}, original:process("?crop=0;0;236;96")) image:copyInto({0, 0}, original:process("?crop=0;0;236;96"))
local checkbox = image:process("?crop=19;26;117;35") local checkbox = image:process("?crop=19;26;117;35")
image:copyInto({119, 26}, checkbox) -- Anti-Aliasing image:copyInto({119, 26}, checkbox) -- Anti-Aliasing
image:copyInto({19, 15}, checkbox) -- Object Lighting image:copyInto({19, 15}, checkbox) -- New Lighting
image:copyInto({119, 15}, checkbox) -- Hardware Cursor image:copyInto({119, 15}, checkbox) -- Hardware Cursor
image:copyInto({119, 68}, image:process("?crop=19;68;117;87")) -- Camera Pan Speed image:copyInto({119, 68}, image:process("?crop=19;68;117;87")) -- Camera Pan Speed

View File

@ -29,12 +29,16 @@ local function getMods(key)
if bindMods[1] then return bindMods end if bindMods[1] then return bindMods end
end end
local function finishBind(type, value) local function finishBind(a, b)
widget.blur("snare") widget.blur("snare")
snared = false snared = false
snareFinished{ type = type, value = value, mods = getMods(value) } if (type(a) == "table") then
for i, mod in ipairs(mods) do snareFinished(a)
mod.active = false else
snareFinished{ type = a, value = b, mods = getMods(value) }
for i, mod in ipairs(mods) do
mod.active = false
end
end end
end end
@ -50,6 +54,8 @@ local function scanInputEvents()
return finishBind("key", data.key) return finishBind("key", data.key)
elseif type == "MouseButtonDown" then elseif type == "MouseButtonDown" then
return finishBind("mouse", data.mouseButton) return finishBind("mouse", data.mouseButton)
elseif type == "ControllerButtonDown" then
return finishBind{ type = "controller", value = data.controllerButton, controller = data.controller }
end end
end end
end end
@ -127,9 +133,9 @@ function bindsToString(binds)
str = str .. v .. " + " str = str .. v .. " + "
end end
end end
if bind.type == "key" then if bind.type == "controller" then
str = str .. bind.value str = str .. "🎮 " .. bind.value
elseif bind.type == "mouse" then else
str = str .. bind.value str = str .. bind.value
end end
local _i = (i - 1) * 2 local _i = (i - 1) * 2
@ -362,7 +368,7 @@ local function initCallbacks()
end end
function init() function init()
--log = sb.logInfo --log = chat and chat.addMessage or sb.logInfo
widget.clearListItems(CATEGORY_LIST_WIDGET) widget.clearListItems(CATEGORY_LIST_WIDGET)
initCallbacks() initCallbacks()

View File

@ -35,9 +35,9 @@ function patch(config)
-- Create anti-aliasing toggle -- Create anti-aliasing toggle
shift(clone(layout, "multiTextureLabel", "antiAliasingLabel"), 98).value = "SUPER-SAMPLED AA" shift(clone(layout, "multiTextureLabel", "antiAliasingLabel"), 98).value = "SUPER-SAMPLED AA"
shift(clone(layout, "multiTextureCheckbox", "antiAliasingCheckbox"), 99) shift(clone(layout, "multiTextureCheckbox", "antiAliasingCheckbox"), 99)
-- Create object lighting toggle -- Create new lighting toggle
shift(clone(layout, "multiTextureLabel", "objectLightingLabel"), 0, -11).value = "NEW OBJECT LIGHTS" shift(clone(layout, "multiTextureLabel", "newLightingLabel"), 0, -11).value = "NEW LIGHTING"
shift(clone(layout, "multiTextureCheckbox", "objectLightingCheckbox"), 0, -11) shift(clone(layout, "multiTextureCheckbox", "newLightingCheckbox"), 0, -11)
-- Create hardware cursor toggle -- Create hardware cursor toggle
shift(clone(layout, "multiTextureLabel", "hardwareCursorLabel"), 98, -11).value = "HARDWARE CURSOR" shift(clone(layout, "multiTextureLabel", "hardwareCursorLabel"), 98, -11).value = "HARDWARE CURSOR"
shift(clone(layout, "multiTextureCheckbox", "hardwareCursorCheckbox"), 99, -11) shift(clone(layout, "multiTextureCheckbox", "hardwareCursorCheckbox"), 99, -11)

View File

@ -1,5 +1,8 @@
{ {
"password" : { "panefeature" : {
"hidden" : true "type" : "panefeature",
} "anchor" : "center",
"positionLocked" : true
},
"password" : { "hidden" : true }
} }

View File

@ -1,22 +0,0 @@
{
"paneLayout" : {
"group" : {
"position" : [8, 71]
},
"search" : {
"type" : "textbox",
"position" : [86, 71],
"hint" : "Search",
"maxWidth" : 50
},
"lblBandInput" : {
"position" : [3, 68]
},
"lblSearchInput" : {
"type" : "image",
"file" : "/interface/songbook/band.png",
"position" : [81, 68],
"zlevel" : -3
}
}
}

View File

@ -0,0 +1,12 @@
function patch(config)
local scrollBG = config.paneLayout.scrollBG
local scrollSize = assets.image(scrollBG.file):size()
config.paneLayout.search = {
type = "textbox",
position = {scrollBG.position[1] + 3,
scrollBG.position[2] + scrollSize[2] - 10},
hint = "^#999;Type here to search for a song",
maxWidth = scrollSize[1] - 6
}
return config
end

View File

@ -8,7 +8,7 @@ function patch(data)
data.backdropImages = jarray{ data.backdropImages = jarray{
jarray{ jarray{
jarray{0, 0}, jarray{0, 0},
"/interface/title/" .. (sb.makeRandomSource():randUInt(100) == 0 and "barst" or "starb") .. "ound.png", "/interface/title/" .. (sb.makeRandomSource():randUInt(300) == 0 and "barst" or "starb") .. "ound.png",
0.5, 0.5,
jarray{0.5, 0.5} jarray{0.5, 0.5}
} }

View File

@ -1,5 +1,5 @@
{ {
"lighting" : { "lighting" : {
"brightnessLimit" : 1.5 "brightnessLimit" : 1.4
} }
} }

View File

@ -1,6 +1,22 @@
--local function drop(color)
-- if type(color) == "table" then
-- for i = 1, #color do
-- color[i] = color[i] * 0.8
-- end
-- end
--end
function patch(object, path) function patch(object, path)
if object.pointLight ~= true and (object.lightColor or object.lightColors) then if object.pointLight ~= true and (object.lightColor or object.lightColors) then
object.lightType = "PointAsSpread" object.lightType = "PointAsSpread"
return object; return object
--elseif type(object.lightColor) == "table" then
-- drop(object.lightColor)
-- return object
--elseif type(object.lightColors) == "table" then
-- for i, v in pairs(object.lightColors) do
-- drop(v)
-- end
-- return object
end end
end end

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

View File

@ -0,0 +1,7 @@
#version 140
in vec2 vertexPosition;
void main() {
gl_Position = vec4(vertexPosition, 0.0, 1.0);
}

View File

@ -1,13 +1,7 @@
{ {
"blitFrameBuffer" : "main", "blitFrameBuffer" : "main",
"effectParameters" : { "effectParameters" : {},
"vertexRounding" : {
"type" : "bool",
"default" : false,
"uniform" : "vertexRounding"
}
},
"effectTextures" : {}, "effectTextures" : {},
"effectShaders" : { "effectShaders" : {

View File

@ -1,4 +1,4 @@
#version 130 #version 140
uniform sampler2D texture0; uniform sampler2D texture0;
uniform sampler2D texture1; uniform sampler2D texture1;
@ -14,13 +14,13 @@ out vec4 outColor;
void main() { void main() {
vec4 texColor; vec4 texColor;
if (fragmentTextureIndex == 3) if (fragmentTextureIndex == 3)
texColor = texture2D(texture3, fragmentTextureCoordinate); texColor = texture(texture3, fragmentTextureCoordinate);
else if (fragmentTextureIndex == 2) else if (fragmentTextureIndex == 2)
texColor = texture2D(texture2, fragmentTextureCoordinate); texColor = texture(texture2, fragmentTextureCoordinate);
else if (fragmentTextureIndex == 1) else if (fragmentTextureIndex == 1)
texColor = texture2D(texture1, fragmentTextureCoordinate); texColor = texture(texture1, fragmentTextureCoordinate);
else else
texColor = texture2D(texture0, fragmentTextureCoordinate); texColor = texture(texture0, fragmentTextureCoordinate);
if (texColor.a <= 0.0) if (texColor.a <= 0.0)
discard; discard;

View File

@ -1,4 +1,4 @@
#version 130 #version 140
uniform vec2 textureSize0; uniform vec2 textureSize0;
uniform vec2 textureSize1; uniform vec2 textureSize1;
@ -6,7 +6,6 @@ uniform vec2 textureSize2;
uniform vec2 textureSize3; uniform vec2 textureSize3;
uniform vec2 screenSize; uniform vec2 screenSize;
uniform mat3 vertexTransform; uniform mat3 vertexTransform;
uniform bool vertexRounding;
in vec2 vertexPosition; in vec2 vertexPosition;
in vec4 vertexColor; in vec4 vertexColor;
@ -21,13 +20,6 @@ void main() {
vec2 screenPosition = (vertexTransform * vec3(vertexPosition, 1.0)).xy; vec2 screenPosition = (vertexTransform * vec3(vertexPosition, 1.0)).xy;
gl_Position = vec4(screenPosition / screenSize * 2.0 - 1.0, 0.0, 1.0); gl_Position = vec4(screenPosition / screenSize * 2.0 - 1.0, 0.0, 1.0);
if (vertexRounding) {
if (((vertexData >> 3) & 0x1) == 1)
screenPosition.x = round(screenPosition.x);
if (((vertexData >> 4) & 0x1) == 1)
screenPosition.y = round(screenPosition.y);
}
int vertexTextureIndex = vertexData & 0x3; int vertexTextureIndex = vertexData & 0x3;
if (vertexTextureIndex == 3) if (vertexTextureIndex == 3)
fragmentTextureCoordinate = vertexTextureCoordinate / textureSize3; fragmentTextureCoordinate = vertexTextureCoordinate / textureSize3;

View File

@ -1,4 +1,4 @@
#version 130 #version 140
uniform sampler2D texture0; uniform sampler2D texture0;
uniform sampler2D texture1; uniform sampler2D texture1;
@ -27,7 +27,7 @@ vec4 cubic(float v) {
return vec4(x, y, z, w); return vec4(x, y, z, w);
} }
vec4 bicubicSample(sampler2D texture, vec2 texcoord, vec2 texscale) { vec4 bicubicSample(sampler2D tex, vec2 texcoord, vec2 texscale) {
texcoord = texcoord - vec2(0.5, 0.5); texcoord = texcoord - vec2(0.5, 0.5);
float fx = fract(texcoord.x); float fx = fract(texcoord.x);
@ -42,10 +42,10 @@ vec4 bicubicSample(sampler2D texture, vec2 texcoord, vec2 texscale) {
vec4 s = vec4(xcubic.x + xcubic.y, xcubic.z + xcubic.w, ycubic.x + ycubic.y, ycubic.z + ycubic.w); vec4 s = vec4(xcubic.x + xcubic.y, xcubic.z + xcubic.w, ycubic.x + ycubic.y, ycubic.z + ycubic.w);
vec4 offset = c + vec4(xcubic.y, xcubic.w, ycubic.y, ycubic.w) / s; vec4 offset = c + vec4(xcubic.y, xcubic.w, ycubic.y, ycubic.w) / s;
vec4 sample0 = texture2D(texture, vec2(offset.x, offset.z) * texscale); vec4 sample0 = texture(tex, vec2(offset.x, offset.z) * texscale);
vec4 sample1 = texture2D(texture, vec2(offset.y, offset.z) * texscale); vec4 sample1 = texture(tex, vec2(offset.y, offset.z) * texscale);
vec4 sample2 = texture2D(texture, vec2(offset.x, offset.w) * texscale); vec4 sample2 = texture(tex, vec2(offset.x, offset.w) * texscale);
vec4 sample3 = texture2D(texture, vec2(offset.y, offset.w) * texscale); vec4 sample3 = texture(tex, vec2(offset.y, offset.w) * texscale);
float sx = s.x / (s.x + s.y); float sx = s.x / (s.x + s.y);
float sy = s.z / (s.z + s.w); float sy = s.z / (s.z + s.w);
@ -67,13 +67,13 @@ vec3 sampleLight(vec2 coord, vec2 scale) {
void main() { void main() {
vec4 texColor; vec4 texColor;
if (fragmentTextureIndex == 3) if (fragmentTextureIndex == 3)
texColor = texture2D(texture3, fragmentTextureCoordinate); texColor = texture(texture3, fragmentTextureCoordinate);
else if (fragmentTextureIndex == 2) else if (fragmentTextureIndex == 2)
texColor = texture2D(texture2, fragmentTextureCoordinate); texColor = texture(texture2, fragmentTextureCoordinate);
else if (fragmentTextureIndex == 1) else if (fragmentTextureIndex == 1)
texColor = texture2D(texture1, fragmentTextureCoordinate); texColor = texture(texture1, fragmentTextureCoordinate);
else else
texColor = texture2D(texture0, fragmentTextureCoordinate); texColor = texture(texture0, fragmentTextureCoordinate);
if (texColor.a <= 0.0) if (texColor.a <= 0.0)
discard; discard;

View File

@ -1,4 +1,4 @@
#version 130 #version 140
uniform vec2 textureSize0; uniform vec2 textureSize0;
uniform vec2 textureSize1; uniform vec2 textureSize1;

View File

Before

Width:  |  Height:  |  Size: 145 B

After

Width:  |  Height:  |  Size: 145 B

View File

Before

Width:  |  Height:  |  Size: 178 B

After

Width:  |  Height:  |  Size: 178 B

View File

Before

Width:  |  Height:  |  Size: 174 B

After

Width:  |  Height:  |  Size: 174 B

View File

@ -1,4 +1,5 @@
-- Revert cursor frames if a mod replaced cursors.png with a SD version again -- Revert cursor frames if a mod replaced cursors.png with a SD version again
-- Otherwise, scale down our HD cursors
if assets.image("/cursors/cursors.png"):size()[1] == 64 then if assets.image("/cursors/cursors.png"):size()[1] == 64 then
local path = "/cursors/opensb/revert.cursor.patch" local path = "/cursors/opensb/revert.cursor.patch"
assets.add(path, '{"scale":null}') assets.add(path, '{"scale":null}')
@ -8,6 +9,14 @@ if assets.image("/cursors/cursors.png"):size()[1] == 64 then
path = "/cursors/opensb/revert.frames.patch" path = "/cursors/opensb/revert.frames.patch"
assets.add(path, '{"frameGrid":{"size":[16,16]}}') assets.add(path, '{"frameGrid":{"size":[16,16]}}')
assets.patch("/cursors/cursors.frames", path) assets.patch("/cursors/cursors.frames", path)
else
local cursors = assets.json("/cursors/cursors.frames:frameGrid.names")
local path = "/cursors/%s.cursor.patch"
for i = 1, #cursors do
for j = 1, #cursors[i] do
assets.add(string.format(path, cursors[i][j]), '{"scale":1}')
end
end
end end
-- Add object patches -- Add object patches
@ -16,3 +25,8 @@ local path = "/objects/opensb/object.patch.lua"
for i = 1, #objects do for i = 1, #objects do
assets.patch(objects[i], path) assets.patch(objects[i], path)
end end
assets.patch(
"/interface/windowconfig/songbook.config",
"/interface/windowconfig/songbook_search_patch.lua"
)

View File

@ -21,6 +21,8 @@ command("run", function(src)
local success, result = pcall(result) local success, result = pcall(result)
if not success then if not success then
return "^#f00;error: " .. result return "^#f00;error: " .. result
elseif result == nil then
return nil
else else
local success, printed = pcall(sb.printJson, result) local success, printed = pcall(sb.printJson, result)
if not success then if not success then

View File

@ -3,41 +3,57 @@ local logHelp = "Available OpenStarbound server commands:\n"
local userHelp = logHelp .. "^cyan;" local userHelp = logHelp .. "^cyan;"
local adminHelp = userHelp local adminHelp = userHelp
local function cmd(name, description, permission, func) local function cmd(meta, func)
local first = next(commands) == nil local first = next(commands) == nil
logHelp = logHelp .. (first and name or ", " .. name) local name = meta.name
userHelp = userHelp .. (first and name or ", ^cyan;" .. name) local description = meta.description
adminHelp = adminHelp .. (first and name or ", ^cyan;" .. name) local permission = meta.permission
if not meta.hidden then
logHelp = logHelp .. (first and name or ", " .. name)
userHelp = userHelp .. (first and name or ", ^cyan;" .. name)
adminHelp = adminHelp .. (first and name or ", ^cyan;" .. name)
end
local keyName = name:lower() local keyName = name:lower()
if permission == "tell" then if permission == "tell" then
commands[keyName] = function(connectionId, ...) commands[keyName] = {meta = meta, func = function(connectionId, ...)
return func(universe.isAdmin(connectionId), connectionId, ...) return func(universe.isAdmin(connectionId), connectionId, ...)
end end}
elseif permission == "admin" then elseif permission == "admin" then
commands[keyName] = function(connectionId, ...) commands[keyName] = {meta = meta, func = function(connectionId, ...)
local error = CommandProcessor.adminCheck(connectionId, description:sub(1, 1):lower() .. description:sub(2)) local error = CommandProcessor.adminCheck(connectionId, description:sub(1, 1):lower() .. description:sub(2))
if error then if error then
return error return error
else else
return func(connectionId, ...) return func(connectionId, ...)
end end
end end}
elseif permission == "user" then elseif permission == "user" then
commands[keyName] = func commands[keyName] = {meta = meta, func = func}
else else
error(string.format("Command '%s' has invalid permission", name)) error(string.format("Command '%s' has invalid permission", name))
end end
end end
cmd("openhelp", "Get help", "tell", function(isAdmin, connectionId) cmd({
name = "openhelp",
description = "Get help",
permission = "tell"
},
function(isAdmin, connectionId)
return isAdmin and adminHelp or userHelp return isAdmin and adminHelp or userHelp
end) end)
do do
local objects = nil local objects = nil
cmd("packetTest", "Do science", "admin", function(connectionId) cmd({
name = "packetTest",
description = "Do science",
permission = "admin",
hidden = true
},
function(connectionId)
if not objects then if not objects then
objects = {} objects = {}
local paths = root.assetsByExtension("object") local paths = root.assetsByExtension("object")
@ -69,7 +85,7 @@ function command(commandName, connectionId, args)
local command = commands[commandName:lower()] local command = commands[commandName:lower()]
if command then if command then
local success, ret = pcall(command, connectionId, table.unpack(args)) local success, ret = pcall(command.func, connectionId, table.unpack(args))
if not success then if not success then
sb.logError("Error in OpenStarbound server command /%s: %s", commandName, ret) sb.logError("Error in OpenStarbound server command /%s: %s", commandName, ret)
return "command error: " .. ret return "command error: " .. ret

415
doc/lua/openstarbound.md Normal file
View File

@ -0,0 +1,415 @@
# Unsorted
These are functions that aren't in any specific table.
---
#### `Maybe<LuaFunction>, Maybe<String>` loadstring(`String` source, [`String` name, [`LuaValue` env]])
Compiles the provided **source** and returns it as a callable function.
If there are any syntax errors, returns `nil` and the error as a string instead.
- **name** is used for error messages, the default is the name of the script that called `loadstring`.
- **env** is used as the environment of the returned function, the default is `_ENV`.
---
# Root
The root table now contains extra asset bindings and bindings to return the tile variant that is used for material and matmods at any position.
---
#### `String[]` root.assetsByExtension(`String` extension)
Returns an array containing all assets with the specified file extension.
By the way, here's a list of every file extension the game does Special Things™ for when loading assets.
<details><summary><b>File Extensions</b></summary>
- Items: `item`, `liqitem`, `matitem`, `miningtool`, `flashlight`, `wiretool`, `beamaxe`, `tillingtool`, `painttool`, `harvestingtool`, `head`, `chest`, `legs`, `back`, `currency`, `consumable`, `blueprint`, `inspectiontool`, `instrument`, `thrownitem`, `unlock`, `activeitem`, `augment`
- Materials: `material`, `matmod`
- Liquids: `liquid`
- NPCs: `npctype`
- Tenants: `tenant`
- Objects: `object`
- Vehicles: `vehicle`
- Monsters: `monstertype`, `monsterpart`, `monsterskill`, `monstercolors`
- Plants: `modularstem`, `modularfoliage`, `grass`, `bush`
- Projectiles: `projectile`
- Particles: `particle`
- Name Gen: `namesource`
- AI Missions: `aimission`
- Quests: `questtemplate`
- Radio Messages: `radiomessages`
- Spawn Types: `spawntypes`
- Species: `species`
- Stagehand: `stagehand`
- Behaviors: `nodes`, `behavior`
- Biomes: `biome`, `weather`
- Terrain: `terrain`
- Treasure: `treasurepools`, `treasurechests`
- Codex Entries: `codex`
- Collections: `collection`
- Statistics: `event`, `achievement`
- Status Effects: `statuseffect`
- Functions: `functions`, `2functions`, `configfunctions`
- Tech: `tech`
- Damage: `damage`
- Dances: `dance`
- Effect Sources: `effectsource`
- Command Macros: `macros`
- Recipes: `recipe`
</details>
#### `String` root.assetData(`String` path)
Returns the raw data of an asset.
#### `String, Maybe<LuaTable>` root.assetOrigin(`String` path, [`bool` getPatches])
Returns the asset source path of an asset, or nil if the asset doesn't exist. If you specify getPatches as true then also returns the patches for the asset as an array, each element containing the source path and patch path in indexes 1 and 2 respectively.
#### `LuaTable` root.assetSourcePaths([`bool` withMetadata])
Without metadata: Returns an array with all the asset source paths.
With metadata: Returns a table, key/value being source path/metadata.
#### `?` root.assetImage(`String` image)
*TODO*
#### `JsonArray` root.assetPatches(`String` asset)
Returns a list of asset sources which patch the specified asset and the paths to those patches.
---
#### `Json` root.getConfiguration(`String` key)
Gets a configuration value in `/storage/starbound.config`.
#### `Json` root.getConfigurationPath(`String` path)
Gets a configuration value in `/storage/starbound.config` by path.
*Both getters will error if you try to get `title`, as that can contain the player's saved server login.*
#### `Json` root.setConfiguration(`String` key, `Json` value)
Sets a configuration value in `/storage/starbound.config`.
#### `Json` root.setConfigurationPath(`String` path, `Json` value)
Sets a configuration value in `/storage/starbound.config` by path.
*Both setters will error if you try to set `safeScripts`, as that can break Starbound's sandbox.*
---
#### `JsonArray` root.allRecipes()
Returns all recipes.
---
# Player
The player table now contains bindings which contains functions to save/load, access and modify the player's identity, mode, aim, emote and more.
---
#### `Json` player.save()
Serializes the player to Json the same way Starbound does for disk storage and returns it.
#### `void` player.load(`Json` save)
Reloads the player from a Json **save**. This will reset active ScriptPanes and scripts running on the player.
---
#### `String` player.name()
Returns the player's name.
#### `void` player.setName(`String` name)
Sets the player's name.
---
#### `String` player.description()
Returns the player's description.
#### `void` player.setDescription(`String` description)
Sets the player's description. The new description will not be networked buntil the player warps or respawns.
---
#### `void` player.setSpecies(`String` species)
Sets the player's species. Must be a valid species.
---
#### `void` player.setGender(`String` gender)
Sets the player's gender.
---
#### `String` player.imagePath()
If the player has a custom humanoid image path set, returns it. otherwise, returns `nil`.
#### `void` player.setImagePath(`String` imagePath)
Sets the player's image path. Specify `nil` to remove the image path.
---
#### `Personality` player.personality()
Returns the player's personality as a `table` containing a `string` idle, `string` armIdle, `Vec2F` headOffset and `Vec2F` armOffset.
#### `void` player.setPersonality(`Personality` personality)
Sets the player's personality. The **personality** must be a `table` containing at least one value as returned by `player.personality()`.
---
#### `String` player.bodyDirectives()
Returns the player's body directives.
#### `void` player.setBodyDirectives(`String` bodyDirectives)
Sets the player's body directives.
---
#### `String` player.emoteDirectives()
Returns the player's emote directives.
#### `void` player.setEmoteDirectives(`String` emoteDirectives)
Sets the player's emote directives.
---
#### `String` player.hair()
Returns the player's hair group.
#### `void` player.setHair(`String` hairGroup)
Sets the player's hair group.
---
#### `String` player.hairType()
Returns the player's hair type.
#### `void` player.setHairType(`String` hairType)
Sets the player's hair type.
---
#### `String` player.hairDirectives()
Returns the player's hair directives.
#### `void` player.setHairDirectives(`String` hairDirectives)
Sets the player's hair directives.
---
#### `String` player.facialHair()
Returns the player's facial hair type. Same as player.facialHairType?
#### `void` player.setFacialHair(`String` facialHairGroup, `String` facialHairType, `String` facialHairDirectives)
Sets the player's facial hair group, type, and directives.
---
#### `String` player.facialHairType()
Returns the player's facial hair type.
#### `void` player.setFacialHairType(`String` facialHairType)
Sets the player's facial hair type.
---
#### `String` player.facialHairGroup()
Returns the player's facial hair group.
#### `void` player.setFacialHairGroup(`String` facialHairGroup)
Sets the player's facial hair group.
---
#### `String` player.facialHairDirectives()
Returns the player's facial hair directives.
#### `void` player.setFacialHairDirectives(`String` facialHairDirectives)
Sets the player's facial hair directives.
---
#### `String` player.facialMask()
Returns the player's facial mask group.
#### `void` player.setFacialMask(`String` facialMaskGroup, `String` facialMaskType, `String` facialMaskDirectives)
Sets the player's facial mask group, type, and directives.
---
#### `String` player.facialMaskDirectives()
Returns the player's facial mask directives.
#### `void` player.setFacialMaskDirectives(`String` facialMaskDirectives)
Sets the player's facial mask directives.
---
#### `PlayerMode` player.mode()
Returns the player's mode.
#### `void` player.setMode(`String` mode)
Sets the player's mode. **mode** must be either `"casual"`, `"survival"` or `"hardcore"`.
---
#### `Color` player.favoriteColor()
Returns the player's favorite color.
It is used for the beam shown when wiring, placing, and highlighting with beam-tools (Matter Manipulator).
#### `void` player.setFavoriteColor(`Color` color)
Sets the player's favorite color. **color** can have an optional fourth value for transparency.
---
#### `Vec2F` player.aimPosition()
Returns the player's aim position.
---
#### `void` player.emote(`String` emote, [`float` cooldown])
Makes the player do an emote with the default cooldown unless a **cooldown** is specified.
#### `String, float` player.currentEmote()
Returns the player's current emote and the seconds left in it.
---
#### `unsigned` player.actionBarGroup()
Returns the player's active action bar.
#### `void` player.setActionBarGroup(`unsigned` barId)
Sets the player's active action bar.
#### `Variant<unsigned, EssentialItem>` player.selectedActionBarSlot()
Returns the player's selected action bar slot.
#### `void` player.setSelectedActionBarSlot(`Variant<unsigned, EssentialItem>` slot)
Sets the player's selected action bar slot.
#### `void` player.setDamageTeam(`DamageTeam` team)
Sets the player's damage team. This must be called every frame to override the current damage team that the server has given the player (normally controlled by /pvp)
---
#### `void` player.say(`String` line)
Makes the player say a string.
---
#### `Json` player.humanoidIdentity()
Returns the specific humanoid identity of the player, containing information such as hair style and idle pose.
#### `void` player.setHumanoidIdentity(`Json` humanoidIdentity)
Sets the specific humanoid identity of the player.
---
#### `ItemDescriptor` player.item(`ItemSlot` itemSlot)
Returns the contents of the specified itemSlot.
#### `void` player.setItem(`ItemSlot` itemSlot, `ItemDescriptor` item)
Puts the specified item into the specified itemSlot.
Item slots in item bags are structured like so: `{String bagName, int slot}`
---
#### `int` player.itemBagSize(`String` itemBagName)
Returns the size of an item bag.
#### `bool` player.itemAllowedInBag(`String` itemBagName, `ItemDescriptor` item)
Returns whether the specified item can enter the specified item bag.
---
#### `ActionBarLink` player.actionBarSlotLink(`int` slot, `String` hand)
Returns the contents of the specified action bar link slot's specified hand.
#### `bool` player.setActionBarSlotLink(`String` itemBagName, `ItemDescriptor` item)
Returns whether the specified item can enter the specified item bag.
---
#### `Float` player.interactRadius()
Returns the player's interact radius.
#### `void` player.setInteractRadius(`Float` interactRadius)
Sets the player's interact radius. This does not persist upon returning to the main menu.
---
#### `JsonArray` player.availableRecipes()
Returns all the recipes the player can craft with their currently held items and currencies.
---

View File

@ -21,6 +21,7 @@ cp \
lib/linux/libsteam_api.so \ lib/linux/libsteam_api.so \
scripts/ci/linux/sbinit.config \ scripts/ci/linux/sbinit.config \
scripts/ci/linux/run-client.sh \ scripts/ci/linux/run-client.sh \
scripts/steam_appid.txt \
client_distribution/linux/ client_distribution/linux/
mkdir -p server_distribution mkdir -p server_distribution
@ -33,11 +34,21 @@ touch server_distribution/mods/mods_go_here
mkdir -p server_distribution/linux mkdir -p server_distribution/linux
# makes the server function on older Linux versions (this is so stupid)
nm --dynamic --undefined-only --with-symbol-versions dist/starbound_server | grep GLIBC_2.29
./scripts/ci/linux/patchelf dist/starbound_server \
--clear-symbol-version exp \
--clear-symbol-version exp2 \
--clear-symbol-version log \
--clear-symbol-version log2 \
--clear-symbol-version pow
cp \ cp \
dist/starbound_server \ dist/starbound_server \
dist/btree_repacker \ dist/btree_repacker \
scripts/ci/linux/run-server.sh \ scripts/ci/linux/run-server.sh \
scripts/ci/linux/sbinit.config \ scripts/ci/linux/sbinit.config \
scripts/steam_appid.txt \
server_distribution/linux/ server_distribution/linux/
tar -cvf dist.tar dist tar -cvf dist.tar dist

BIN
scripts/ci/linux/patchelf Executable file

Binary file not shown.

View File

@ -24,4 +24,5 @@ cp \
dist/planet_mapgen \ dist/planet_mapgen \
scripts/ci/macos/sbinit.config \ scripts/ci/macos/sbinit.config \
scripts/ci/macos/run-server.sh \ scripts/ci/macos/run-server.sh \
scripts/steam_appid.txt \
client_distribution/osx/ client_distribution/osx/

View File

@ -8,6 +8,7 @@ mkdir %client%\mods
mkdir %client%\logs mkdir %client%\logs
mkdir %client%\assets mkdir %client%\assets
mkdir %client%\win mkdir %client%\win
echo 211820 > %client%\win\steam_appid.txt
set server=server_distribution set server=server_distribution
if exist %server% rmdir %server% /S /Q if exist %server% rmdir %server% /S /Q

View File

@ -136,6 +136,8 @@ endif()
option(STAR_LUA_APICHECK "Use lua api checks" OFF) option(STAR_LUA_APICHECK "Use lua api checks" OFF)
option(STAR_USE_JEMALLOC "Use jemalloc allocators" OFF) option(STAR_USE_JEMALLOC "Use jemalloc allocators" OFF)
option(STAR_USE_MIMALLOC "Use mimalloc allocators" OFF)
option(STAR_USE_RPMALLOC "Use rpmalloc allocators" OFF)
# Report all the discovered system / environment settings and all options. # Report all the discovered system / environment settings and all options.
@ -174,6 +176,8 @@ endif()
message(STATUS "Using Lua API checks: ${STAR_LUA_APICHECK}") message(STATUS "Using Lua API checks: ${STAR_LUA_APICHECK}")
message(STATUS "Using jemalloc: ${STAR_USE_JEMALLOC}") message(STATUS "Using jemalloc: ${STAR_USE_JEMALLOC}")
message(STATUS "Using mimalloc: ${STAR_USE_MIMALLOC}")
message(STATUS "Using rpmalloc: ${STAR_USE_RPMALLOC}")
# Set C defines and cmake variables based on the build settings we have now # Set C defines and cmake variables based on the build settings we have now
# determined... # determined...
@ -244,6 +248,10 @@ endif()
if(STAR_USE_JEMALLOC) if(STAR_USE_JEMALLOC)
add_definitions(-DSTAR_USE_JEMALLOC) add_definitions(-DSTAR_USE_JEMALLOC)
elseif(STAR_USE_MIMALLOC)
add_definitions(-DSTAR_USE_MIMALLOC)
elseif(STAR_USE_RPMALLOC)
add_definitions(-DSTAR_USE_RPMALLOC -DENABLE_PRELOAD)
endif() endif()
# Set C/C++ compiler flags based on build environment... # Set C/C++ compiler flags based on build environment...
@ -449,6 +457,11 @@ if(STAR_USE_JEMALLOC)
set(STAR_EXT_LIBS ${JEMALLOC_LIBRARY}) set(STAR_EXT_LIBS ${JEMALLOC_LIBRARY})
endif() endif()
if (STAR_USE_MIMALLOC)
find_package(mimalloc CONFIG REQUIRED)
set(STAR_EXT_LIBS ${STAR_EXT_LIBS} $<IF:$<TARGET_EXISTS:mimalloc-static>,mimalloc-static,mimalloc>)
endif()
find_package(ZLIB REQUIRED) find_package(ZLIB REQUIRED)
find_package(PNG REQUIRED) find_package(PNG REQUIRED)
find_package(Freetype REQUIRED) find_package(Freetype REQUIRED)

View File

@ -31,7 +31,8 @@
"VCPKG_TARGET_TRIPLET": "x64-windows-mixed-md", "VCPKG_TARGET_TRIPLET": "x64-windows-mixed-md",
"CMAKE_MSVC_RUNTIME_LIBRARY": "MultiThreaded$<$<CONFIG:Debug>:Debug>DLL", "CMAKE_MSVC_RUNTIME_LIBRARY": "MultiThreaded$<$<CONFIG:Debug>:Debug>DLL",
"CMAKE_INCLUDE_PATH": "${sourceParentDir}/lib/windows/include", "CMAKE_INCLUDE_PATH": "${sourceParentDir}/lib/windows/include",
"CMAKE_LIBRARY_PATH": "${sourceParentDir}/lib/windows" "CMAKE_LIBRARY_PATH": "${sourceParentDir}/lib/windows",
"STAR_USE_RPMALLOC": true
}, },
"vendor": { "vendor": {
"microsoft.com/VisualStudioSettings/CMake/1.0": { "microsoft.com/VisualStudioSettings/CMake/1.0": {

View File

@ -18,6 +18,10 @@ namespace Star {
LPWSTR* argsList = CommandLineToArgvW(GetCommandLineW(), &nArgs); \ LPWSTR* argsList = CommandLineToArgvW(GetCommandLineW(), &nArgs); \
Star::StringList args; \ Star::StringList args; \
for (int i = 0; i < nArgs; ++i) args.append(Star::String(argsList[i])); \ for (int i = 0; i < nArgs; ++i) args.append(Star::String(argsList[i])); \
if (IsDebuggerPresent() && AllocConsole()) { \
freopen("CONOUT$", "w", stdout); \
freopen("CONOUT$", "w", stderr); \
} \
return Star::runMainApplication(Star::make_unique<ApplicationClass>(), args); \ return Star::runMainApplication(Star::make_unique<ApplicationClass>(), args); \
} }

View File

@ -90,23 +90,23 @@ Maybe<Key> keyFromSdlKeyCode(SDL_Keycode sym) {
{SDLK_y, Key::Y}, {SDLK_y, Key::Y},
{SDLK_z, Key::Z}, {SDLK_z, Key::Z},
{SDLK_DELETE, Key::Delete}, {SDLK_DELETE, Key::Delete},
{SDLK_KP_0, Key::Kp0}, {SDLK_KP_0, Key::Keypad0},
{SDLK_KP_1, Key::Kp1}, {SDLK_KP_1, Key::Keypad1},
{SDLK_KP_2, Key::Kp2}, {SDLK_KP_2, Key::Keypad2},
{SDLK_KP_3, Key::Kp3}, {SDLK_KP_3, Key::Keypad3},
{SDLK_KP_4, Key::Kp4}, {SDLK_KP_4, Key::Keypad4},
{SDLK_KP_5, Key::Kp5}, {SDLK_KP_5, Key::Keypad5},
{SDLK_KP_6, Key::Kp6}, {SDLK_KP_6, Key::Keypad6},
{SDLK_KP_7, Key::Kp7}, {SDLK_KP_7, Key::Keypad7},
{SDLK_KP_8, Key::Kp8}, {SDLK_KP_8, Key::Keypad8},
{SDLK_KP_9, Key::Kp9}, {SDLK_KP_9, Key::Keypad9},
{SDLK_KP_PERIOD, Key::Kp_period}, {SDLK_KP_PERIOD, Key::KeypadPeriod},
{SDLK_KP_DIVIDE, Key::Kp_divide}, {SDLK_KP_DIVIDE, Key::KeypadDivide},
{SDLK_KP_MULTIPLY, Key::Kp_multiply}, {SDLK_KP_MULTIPLY, Key::KeypadMultiply},
{SDLK_KP_MINUS, Key::Kp_minus}, {SDLK_KP_MINUS, Key::KeypadMinus},
{SDLK_KP_PLUS, Key::Kp_plus}, {SDLK_KP_PLUS, Key::KeypadPlus},
{SDLK_KP_ENTER, Key::Kp_enter}, {SDLK_KP_ENTER, Key::KeypadEnter},
{SDLK_KP_EQUALS, Key::Kp_equals}, {SDLK_KP_EQUALS, Key::KeypadEquals},
{SDLK_UP, Key::Up}, {SDLK_UP, Key::Up},
{SDLK_DOWN, Key::Down}, {SDLK_DOWN, Key::Down},
{SDLK_RIGHT, Key::Right}, {SDLK_RIGHT, Key::Right},
@ -131,6 +131,15 @@ Maybe<Key> keyFromSdlKeyCode(SDL_Keycode sym) {
{SDLK_F13, Key::F13}, {SDLK_F13, Key::F13},
{SDLK_F14, Key::F14}, {SDLK_F14, Key::F14},
{SDLK_F15, Key::F15}, {SDLK_F15, Key::F15},
{SDLK_F16, Key::F16},
{SDLK_F17, Key::F17},
{SDLK_F18, Key::F18},
{SDLK_F19, Key::F19},
{SDLK_F20, Key::F20},
{SDLK_F21, Key::F21},
{SDLK_F22, Key::F22},
{SDLK_F23, Key::F23},
{SDLK_F24, Key::F24},
{SDLK_NUMLOCKCLEAR, Key::NumLock}, {SDLK_NUMLOCKCLEAR, Key::NumLock},
{SDLK_CAPSLOCK, Key::CapsLock}, {SDLK_CAPSLOCK, Key::CapsLock},
{SDLK_SCROLLLOCK, Key::ScrollLock}, {SDLK_SCROLLLOCK, Key::ScrollLock},
@ -235,7 +244,9 @@ public:
SDL_free(basePath); SDL_free(basePath);
} }
#if SDL_VERSION_ATLEAST(2, 0, 18)
SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1"); SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1");
#endif
m_signalHandler.setHandleInterrupt(true); m_signalHandler.setHandleInterrupt(true);
m_signalHandler.setHandleFatal(true); m_signalHandler.setHandleFatal(true);
@ -304,6 +315,10 @@ public:
SDL_GetWindowSize(m_sdlWindow, &width, &height); SDL_GetWindowSize(m_sdlWindow, &width, &height);
m_windowSize = Vec2U(width, height); m_windowSize = Vec2U(width, height);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
m_sdlGlContext = SDL_GL_CreateContext(m_sdlWindow); m_sdlGlContext = SDL_GL_CreateContext(m_sdlWindow);
if (!m_sdlGlContext) if (!m_sdlGlContext)
throw ApplicationException::format("Application: Could not create OpenGL context: {}", SDL_GetError()); throw ApplicationException::format("Application: Could not create OpenGL context: {}", SDL_GetError());

View File

@ -81,7 +81,11 @@ void PcP2PNetworkingService::setJoinRemote(HostAddressWithPort location) {
setJoinLocation(JoinRemote(location)); setJoinLocation(JoinRemote(location));
} }
void Star::PcP2PNetworkingService::setActivityData([[maybe_unused]] String const& title, [[maybe_unused]] Maybe<pair<uint16_t, uint16_t>> party) { void Star::PcP2PNetworkingService::setActivityData(
[[maybe_unused]] const char* title,
[[maybe_unused]] const char* details,
[[maybe_unused]] int64_t startTime,
[[maybe_unused]] Maybe<pair<uint16_t, uint16_t>> party) {
#ifdef STAR_ENABLE_DISCORD_INTEGRATION #ifdef STAR_ENABLE_DISCORD_INTEGRATION
MutexLocker discordLocker(m_state->discordMutex); MutexLocker discordLocker(m_state->discordMutex);
#endif #endif
@ -92,19 +96,25 @@ void Star::PcP2PNetworkingService::setActivityData([[maybe_unused]] String const
if (m_discordUpdatingActivity) if (m_discordUpdatingActivity)
return; return;
if (title != m_discordActivityTitle || party != m_discordPartySize || m_discordForceUpdateActivity) { if (title != m_discordActivityTitle
|| details != m_discordActivityDetails
|| startTime != m_discordActivityStartTime || party != m_discordPartySize || m_discordForceUpdateActivity) {
m_discordForceUpdateActivity = false; m_discordForceUpdateActivity = false;
m_discordPartySize = party; m_discordPartySize = party;
m_discordActivityTitle = title; m_discordActivityTitle = title;
m_discordActivityDetails = details;
m_discordActivityStartTime = startTime;
discord::Activity activity = {}; discord::Activity activity = {};
activity.SetType(discord::ActivityType::Playing); activity.SetType(discord::ActivityType::Playing);
activity.SetName("Starbound"); activity.SetName("Starbound");
activity.SetState(title.utf8Ptr()); activity.SetState(title);
activity.SetDetails(details);
if (auto p = party) { activity.GetTimestamps().SetStart(startTime);
activity.GetParty().GetSize().SetCurrentSize(p->first); if (party) {
activity.GetParty().GetSize().SetMaxSize(p->second); auto& size = activity.GetParty().GetSize();
size.SetCurrentSize(party->first);
size.SetMaxSize(party->second);
} }
if (auto lobby = m_discordServerLobby) if (auto lobby = m_discordServerLobby)

View File

@ -18,7 +18,7 @@ public:
void setJoinUnavailable() override; void setJoinUnavailable() override;
void setJoinLocal(uint32_t capacity) override; void setJoinLocal(uint32_t capacity) override;
void setJoinRemote(HostAddressWithPort location) override; void setJoinRemote(HostAddressWithPort location) override;
void setActivityData(String const& title, Maybe<pair<uint16_t, uint16_t>>) override; void setActivityData(const char* title, const char* details, int64_t startTime, Maybe<pair<uint16_t, uint16_t>>) override;
MVariant<P2PNetworkingPeerId, HostAddressWithPort> pullPendingJoin() override; MVariant<P2PNetworkingPeerId, HostAddressWithPort> pullPendingJoin() override;
Maybe<pair<String, RpcPromiseKeeper<P2PJoinRequestReply>>> pullJoinRequest() override; Maybe<pair<String, RpcPromiseKeeper<P2PJoinRequestReply>>> pullJoinRequest() override;
@ -125,6 +125,8 @@ private:
HashMap<discord::UserId, DiscordP2PSocket*> m_discordOpenSockets; HashMap<discord::UserId, DiscordP2PSocket*> m_discordOpenSockets;
String m_discordActivityTitle; String m_discordActivityTitle;
String m_discordActivityDetails;
int64_t m_discordActivityStartTime = 0;
Maybe<pair<uint16_t, uint16_t>> m_discordPartySize; Maybe<pair<uint16_t, uint16_t>> m_discordPartySize;
bool m_discordForceUpdateActivity = false; bool m_discordForceUpdateActivity = false;
bool m_discordUpdatingActivity = false; bool m_discordUpdatingActivity = false;

View File

@ -8,7 +8,7 @@ namespace Star {
size_t const MultiTextureCount = 4; size_t const MultiTextureCount = 4;
char const* DefaultVertexShader = R"SHADER( char const* DefaultVertexShader = R"SHADER(
#version 130 #version 140
uniform vec2 textureSize0; uniform vec2 textureSize0;
uniform vec2 textureSize1; uniform vec2 textureSize1;
@ -49,7 +49,7 @@ void main() {
)SHADER"; )SHADER";
char const* DefaultFragmentShader = R"SHADER( char const* DefaultFragmentShader = R"SHADER(
#version 130 #version 140
uniform sampler2D texture0; uniform sampler2D texture0;
uniform sampler2D texture1; uniform sampler2D texture1;
@ -65,13 +65,13 @@ out vec4 outColor;
void main() { void main() {
vec4 texColor; vec4 texColor;
if (fragmentTextureIndex == 3) if (fragmentTextureIndex == 3)
texColor = texture2D(texture3, fragmentTextureCoordinate); texColor = texture(texture3, fragmentTextureCoordinate);
else if (fragmentTextureIndex == 2) else if (fragmentTextureIndex == 2)
texColor = texture2D(texture2, fragmentTextureCoordinate); texColor = texture(texture2, fragmentTextureCoordinate);
else if (fragmentTextureIndex == 1) else if (fragmentTextureIndex == 1)
texColor = texture2D(texture1, fragmentTextureCoordinate); texColor = texture(texture1, fragmentTextureCoordinate);
else else
texColor = texture2D(texture0, fragmentTextureCoordinate); texColor = texture(texture0, fragmentTextureCoordinate);
if (texColor.a <= 0.0) if (texColor.a <= 0.0)
discard; discard;
@ -80,9 +80,19 @@ void main() {
} }
)SHADER"; )SHADER";
/*
static void GLAPIENTRY GlMessageCallback(GLenum, GLenum type, GLuint, GLenum, GLsizei, const GLchar* message, const void* renderer) {
if (type == GL_DEBUG_TYPE_ERROR) {
Logger::error("GL ERROR: {}", message);
__debugbreak();
}
}
*/
OpenGlRenderer::OpenGlRenderer() { OpenGlRenderer::OpenGlRenderer() {
if (glewInit() != GLEW_OK) auto glewResult = glewInit();
throw RendererException("Could not initialize GLEW"); if (glewResult != GLEW_OK && glewResult != GLEW_ERROR_NO_GLX_DISPLAY)
throw RendererException::format("Could not initialize GLEW: {}", (char*)glewGetErrorString(glewResult));
if (!GLEW_VERSION_2_0) if (!GLEW_VERSION_2_0)
throw RendererException("OpenGL 2.0 not available!"); throw RendererException("OpenGL 2.0 not available!");
@ -94,10 +104,11 @@ OpenGlRenderer::OpenGlRenderer() {
(const char*)glGetString(GL_SHADING_LANGUAGE_VERSION)); (const char*)glGetString(GL_SHADING_LANGUAGE_VERSION));
glClearColor(0.0, 0.0, 0.0, 1.0); glClearColor(0.0, 0.0, 0.0, 1.0);
glEnable(GL_TEXTURE_2D);
glEnable(GL_BLEND); glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_DEPTH_TEST); glDisable(GL_DEPTH_TEST);
//glEnable(GL_DEBUG_OUTPUT);
//glDebugMessageCallback(GlMessageCallback, this);
m_whiteTexture = createGlTexture(Image::filled({1, 1}, Vec4B(255, 255, 255, 255), PixelFormat::RGBA32), m_whiteTexture = createGlTexture(Image::filled({1, 1}, Vec4B(255, 255, 255, 255), PixelFormat::RGBA32),
TextureAddressing::Clamp, TextureAddressing::Clamp,
@ -142,12 +153,32 @@ OpenGlRenderer::GlFrameBuffer::GlFrameBuffer(Json const& fbConfig) : config(fbCo
GLenum target = multisample ? GL_TEXTURE_2D_MULTISAMPLE : GL_TEXTURE_2D; GLenum target = multisample ? GL_TEXTURE_2D_MULTISAMPLE : GL_TEXTURE_2D;
glBindTexture(target, texture->glTextureId()); glBindTexture(target, texture->glTextureId());
Vec2U size = jsonToVec2U(config.getArray("size", { 256, 256 })); sizeDiv = config.getUInt("sizeDiv", 1);
Vec2U size = jsonToVec2U(config.getArray("size", { 256, 256 })) / sizeDiv;
if (multisample) if (multisample)
glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, multisample, GL_RGBA8, size[0], size[1], GL_TRUE); glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, multisample, GL_RGBA8, size[0], size[1], GL_TRUE);
else else {
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, size[0], size[1], 0, GL_RGB, GL_UNSIGNED_BYTE, NULL); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, size[0], size[1], 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
}
auto addressing = TextureAddressingNames.getLeft(config.getString("textureAddressing", "clamp"));
auto filtering = TextureFilteringNames.getLeft(config.getString("textureFiltering", "nearest"));
if (addressing == TextureAddressing::Clamp) {
glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
} else {
glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_REPEAT);
}
if (!multisample) {
if (filtering == TextureFiltering::Nearest) {
glTexParameterf(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameterf(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
} else {
glTexParameterf(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
}
}
glGenFramebuffers(1, &id); glGenFramebuffers(1, &id);
if (!id) if (!id)
@ -173,6 +204,7 @@ void OpenGlRenderer::loadConfig(Json const& config) {
for (auto& pair : config.getObject("frameBuffers", {})) { for (auto& pair : config.getObject("frameBuffers", {})) {
Json config = pair.second; Json config = pair.second;
config = config.set("multisample", m_multiSampling); config = config.set("multisample", m_multiSampling);
Logger::info("Creating framebuffer {}", pair.first);
m_frameBuffers[pair.first] = make_ref<GlFrameBuffer>(config); m_frameBuffers[pair.first] = make_ref<GlFrameBuffer>(config);
} }
@ -246,15 +278,16 @@ void OpenGlRenderer::loadEffectConfig(String const& name, Json const& effectConf
auto& effect = m_effects.emplace(name, Effect()).first->second; auto& effect = m_effects.emplace(name, Effect()).first->second;
effect.program = m_program; effect.program = m_program;
effect.config = effectConfig; effect.config = effectConfig;
effect.includeVBTextures = effectConfig.getBool("includeVBTextures",true);
m_currentEffect = &effect; m_currentEffect = &effect;
setupGlUniforms(effect); setupGlUniforms(effect, m_screenSize);
for (auto const& p : effectConfig.getObject("effectParameters", {})) { for (auto const& p : effectConfig.getObject("effectParameters", {})) {
EffectParameter effectParameter; EffectParameter effectParameter;
effectParameter.parameterUniform = glGetUniformLocation(m_program, p.second.getString("uniform").utf8Ptr()); effectParameter.parameterUniform = glGetUniformLocation(m_program, p.second.getString("uniform").utf8Ptr());
if (effectParameter.parameterUniform == -1) { if (effectParameter.parameterUniform == -1) {
Logger::warn("OpenGL20 effect parameter '{}' has no associated uniform, skipping", p.first); Logger::warn("OpenGL20 effect parameter '{}' in effect '{}' has no associated uniform, skipping", p.first, name);
} else { } else {
String type = p.second.getString("type"); String type = p.second.getString("type");
if (type == "bool") { if (type == "bool") {
@ -296,7 +329,7 @@ void OpenGlRenderer::loadEffectConfig(String const& name, Json const& effectConf
// Assign each texture parameter a texture unit starting with MultiTextureCount, the first // Assign each texture parameter a texture unit starting with MultiTextureCount, the first
// few texture units are used by the primary textures being drawn. Currently, // few texture units are used by the primary textures being drawn. Currently,
// maximum texture units are not checked. // maximum texture units are not checked.
unsigned parameterTextureUnit = MultiTextureCount; unsigned parameterTextureUnit = effect.includeVBTextures ? MultiTextureCount : 0;
for (auto const& p : effectConfig.getObject("effectTextures", {})) { for (auto const& p : effectConfig.getObject("effectTextures", {})) {
EffectTexture effectTexture; EffectTexture effectTexture;
@ -383,19 +416,39 @@ bool OpenGlRenderer::switchEffectConfig(String const& name) {
if (auto blitFrameBufferId = effect.config.optString("blitFrameBuffer")) if (auto blitFrameBufferId = effect.config.optString("blitFrameBuffer"))
blitGlFrameBuffer(getGlFrameBuffer(*blitFrameBufferId)); blitGlFrameBuffer(getGlFrameBuffer(*blitFrameBufferId));
if (auto frameBufferId = effect.config.optString("frameBuffer")) auto effectScreenSize = m_screenSize;
switchGlFrameBuffer(getGlFrameBuffer(*frameBufferId)); if (auto frameBufferId = effect.config.optString("frameBuffer")) {
else { auto buf = getGlFrameBuffer(*frameBufferId);
switchGlFrameBuffer(buf);
effectScreenSize = m_screenSize / (buf->sizeDiv);
} else {
m_currentFrameBuffer.reset(); m_currentFrameBuffer.reset();
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
} }
glUseProgram(m_program = effect.program); glUseProgram(m_program = effect.program);
setupGlUniforms(effect); setupGlUniforms(effect, effectScreenSize);
m_currentEffect = &effect; m_currentEffect = &effect;
setEffectParameter("vertexRounding", m_multiSampling > 0); setEffectParameter("vertexRounding", m_multiSampling > 0);
if (auto fbts = effect.config.optArray("frameBufferTextures")) {
for (auto const& fbt : *fbts) {
if (auto frameBufferId = fbt.optString("framebuffer")) {
auto textureUniform = fbt.getString("texture");
auto ptr = m_currentEffect->textures.ptr(textureUniform);
if (ptr) {
if (!ptr->textureValue || ptr->textureValue->textureId == 0) {
auto texture = getGlFrameBuffer(*frameBufferId)->texture;
ptr->textureValue = texture;
if (ptr->textureSizeUniform != -1) {
auto textureSize = ptr->textureValue->glTextureSize();
glUniform2f(ptr->textureSizeUniform, textureSize[0], textureSize[1]);
}
}
}
}
}
}
return true; return true;
} }
@ -494,12 +547,13 @@ void OpenGlRenderer::setScreenSize(Vec2U screenSize) {
glUniform2f(m_screenSizeUniform, m_screenSize[0], m_screenSize[1]); glUniform2f(m_screenSizeUniform, m_screenSize[0], m_screenSize[1]);
for (auto& frameBuffer : m_frameBuffers) { for (auto& frameBuffer : m_frameBuffers) {
unsigned sizeDiv = frameBuffer.second->sizeDiv;
if (unsigned multisample = frameBuffer.second->multisample) { if (unsigned multisample = frameBuffer.second->multisample) {
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, frameBuffer.second->texture->glTextureId()); glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, frameBuffer.second->texture->glTextureId());
glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, multisample, GL_RGBA8, m_screenSize[0], m_screenSize[1], GL_TRUE); glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, multisample, GL_RGBA8, m_screenSize[0] / sizeDiv, m_screenSize[1] / sizeDiv, GL_TRUE);
} else { } else {
glBindTexture(GL_TEXTURE_2D, frameBuffer.second->texture->glTextureId()); glBindTexture(GL_TEXTURE_2D, frameBuffer.second->texture->glTextureId());
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, m_screenSize[0], m_screenSize[1], 0, GL_RGB, GL_UNSIGNED_BYTE, NULL); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, m_screenSize[0] / sizeDiv, m_screenSize[1] / sizeDiv, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
} }
} }
} }
@ -694,6 +748,10 @@ Vec2U OpenGlRenderer::GlLoneTexture::glTextureCoordinateOffset() const {
return Vec2U(); return Vec2U();
} }
OpenGlRenderer::GlRenderBuffer::GlRenderBuffer() {
glGenVertexArrays(1, &vertexArray);
}
OpenGlRenderer::GlRenderBuffer::~GlRenderBuffer() { OpenGlRenderer::GlRenderBuffer::~GlRenderBuffer() {
for (auto const& texture : usedTextures) { for (auto const& texture : usedTextures) {
if (auto gt = as<GlGroupedTexture>(texture.get())) if (auto gt = as<GlGroupedTexture>(texture.get()))
@ -701,6 +759,7 @@ OpenGlRenderer::GlRenderBuffer::~GlRenderBuffer() {
} }
for (auto const& vb : vertexBuffers) for (auto const& vb : vertexBuffers)
glDeleteBuffers(1, &vb.vertexBuffer); glDeleteBuffers(1, &vb.vertexBuffer);
glDeleteVertexArrays(1, &vertexArray);
} }
void OpenGlRenderer::GlRenderBuffer::set(List<RenderPrimitive>& primitives) { void OpenGlRenderer::GlRenderBuffer::set(List<RenderPrimitive>& primitives) {
@ -715,7 +774,7 @@ void OpenGlRenderer::GlRenderBuffer::set(List<RenderPrimitive>& primitives) {
List<GLuint> currentTextures; List<GLuint> currentTextures;
List<Vec2U> currentTextureSizes; List<Vec2U> currentTextureSizes;
size_t currentVertexCount = 0; size_t currentVertexCount = 0;
glBindVertexArray(vertexArray);
auto finishCurrentBuffer = [&]() { auto finishCurrentBuffer = [&]() {
if (currentVertexCount > 0) { if (currentVertexCount > 0) {
GlVertexBuffer vb; GlVertexBuffer vb;
@ -947,10 +1006,12 @@ void OpenGlRenderer::renderGlBuffer(GlRenderBuffer const& renderBuffer, Mat3F co
for (auto const& vb : renderBuffer.vertexBuffers) { for (auto const& vb : renderBuffer.vertexBuffers) {
glUniformMatrix3fv(m_vertexTransformUniform, 1, GL_TRUE, transformation.ptr()); glUniformMatrix3fv(m_vertexTransformUniform, 1, GL_TRUE, transformation.ptr());
for (size_t i = 0; i < vb.textures.size(); ++i) { if (m_currentEffect->includeVBTextures) {
glUniform2f(m_textureSizeUniforms[i], vb.textures[i].size[0], vb.textures[i].size[1]); for (size_t i = 0; i < vb.textures.size(); ++i) {
glActiveTexture(GL_TEXTURE0 + i); glUniform2f(m_textureSizeUniforms[i], vb.textures[i].size[0], vb.textures[i].size[1]);
glBindTexture(GL_TEXTURE_2D, vb.textures[i].texture); glActiveTexture(GL_TEXTURE0 + i);
glBindTexture(GL_TEXTURE_2D, vb.textures[i].texture);
}
} }
for (auto const& p : m_currentEffect->textures) { for (auto const& p : m_currentEffect->textures) {
@ -977,7 +1038,7 @@ void OpenGlRenderer::renderGlBuffer(GlRenderBuffer const& renderBuffer, Mat3F co
} }
//Assumes the passed effect program is currently in use. //Assumes the passed effect program is currently in use.
void OpenGlRenderer::setupGlUniforms(Effect& effect) { void OpenGlRenderer::setupGlUniforms(Effect& effect, Vec2U screenSize) {
m_positionAttribute = effect.getAttribute("vertexPosition"); m_positionAttribute = effect.getAttribute("vertexPosition");
m_colorAttribute = effect.getAttribute("vertexColor"); m_colorAttribute = effect.getAttribute("vertexColor");
m_texCoordAttribute = effect.getAttribute("vertexTextureCoordinate"); m_texCoordAttribute = effect.getAttribute("vertexTextureCoordinate");
@ -985,17 +1046,21 @@ void OpenGlRenderer::setupGlUniforms(Effect& effect) {
m_textureUniforms.clear(); m_textureUniforms.clear();
m_textureSizeUniforms.clear(); m_textureSizeUniforms.clear();
for (size_t i = 0; i < MultiTextureCount; ++i) { if (effect.includeVBTextures) {
m_textureUniforms.append(effect.getUniform(strf("texture{}", i).c_str())); for (size_t i = 0; i < MultiTextureCount; ++i) {
m_textureSizeUniforms.append(effect.getUniform(strf("textureSize{}", i).c_str())); m_textureUniforms.append(effect.getUniform(strf("texture{}", i).c_str()));
m_textureSizeUniforms.append(effect.getUniform(strf("textureSize{}", i).c_str()));
}
} }
m_screenSizeUniform = effect.getUniform("screenSize"); m_screenSizeUniform = effect.getUniform("screenSize");
m_vertexTransformUniform = effect.getUniform("vertexTransform"); m_vertexTransformUniform = effect.getUniform("vertexTransform");
for (size_t i = 0; i < MultiTextureCount; ++i) if (effect.includeVBTextures) {
glUniform1i(m_textureUniforms[i], i); for (size_t i = 0; i < MultiTextureCount; ++i)
glUniform1i(m_textureUniforms[i], i);
}
glUniform2f(m_screenSizeUniform, m_screenSize[0], m_screenSize[1]); glUniform2f(m_screenSizeUniform, screenSize[0], screenSize[1]);
} }
RefPtr<OpenGlRenderer::GlFrameBuffer> OpenGlRenderer::getGlFrameBuffer(String const& id) { RefPtr<OpenGlRenderer::GlFrameBuffer> OpenGlRenderer::getGlFrameBuffer(String const& id) {

View File

@ -143,6 +143,7 @@ private:
size_t vertexCount = 0; size_t vertexCount = 0;
}; };
GlRenderBuffer();
~GlRenderBuffer(); ~GlRenderBuffer();
void set(List<RenderPrimitive>& primitives) override; void set(List<RenderPrimitive>& primitives) override;
@ -152,6 +153,7 @@ private:
HashSet<TexturePtr> usedTextures; HashSet<TexturePtr> usedTextures;
List<GlVertexBuffer> vertexBuffers; List<GlVertexBuffer> vertexBuffers;
GLuint vertexArray = 0;
bool useMultiTexturing{true}; bool useMultiTexturing{true};
}; };
@ -178,6 +180,7 @@ private:
Json config; Json config;
bool blitted = false; bool blitted = false;
unsigned multisample = 0; unsigned multisample = 0;
unsigned sizeDiv = 1;
GlFrameBuffer(Json const& config); GlFrameBuffer(Json const& config);
~GlFrameBuffer(); ~GlFrameBuffer();
@ -195,6 +198,7 @@ private:
GLuint getAttribute(String const& name); GLuint getAttribute(String const& name);
GLuint getUniform(String const& name); GLuint getUniform(String const& name);
bool includeVBTextures;
}; };
static bool logGlErrorSummary(String prefix); static bool logGlErrorSummary(String prefix);
@ -209,7 +213,7 @@ private:
void renderGlBuffer(GlRenderBuffer const& renderBuffer, Mat3F const& transformation); void renderGlBuffer(GlRenderBuffer const& renderBuffer, Mat3F const& transformation);
void setupGlUniforms(Effect& effect); void setupGlUniforms(Effect& effect, Vec2U screenSize);
RefPtr<OpenGlRenderer::GlFrameBuffer> getGlFrameBuffer(String const& id); RefPtr<OpenGlRenderer::GlFrameBuffer> getGlFrameBuffer(String const& id);
void blitGlFrameBuffer(RefPtr<OpenGlRenderer::GlFrameBuffer> const& frameBuffer); void blitGlFrameBuffer(RefPtr<OpenGlRenderer::GlFrameBuffer> const& frameBuffer);

View File

@ -104,6 +104,7 @@ Maybe<RectU> FramesSpecification::getRect(String const& frame) const {
Assets::Assets(Settings settings, StringList assetSources) { Assets::Assets(Settings settings, StringList assetSources) {
const char* AssetsPatchSuffix = ".patch"; const char* AssetsPatchSuffix = ".patch";
const char* AssetsPatchListSuffix = ".patchlist";
const char* AssetsLuaPatchSuffix = ".patch.lua"; const char* AssetsLuaPatchSuffix = ".patch.lua";
m_settings = std::move(settings); m_settings = std::move(settings);
@ -123,6 +124,7 @@ Assets::Assets(Settings settings, StringList assetSources) {
LuaCallbacks callbacks; LuaCallbacks callbacks;
callbacks.registerCallbackWithSignature<StringSet, String>("byExtension", bind(&Assets::scanExtension, this, _1)); callbacks.registerCallbackWithSignature<StringSet, String>("byExtension", bind(&Assets::scanExtension, this, _1));
callbacks.registerCallbackWithSignature<Json, String>("json", bind(&Assets::json, this, _1)); callbacks.registerCallbackWithSignature<Json, String>("json", bind(&Assets::json, this, _1));
callbacks.registerCallbackWithSignature<bool, String>("exists", bind(&Assets::assetExists, this, _1));
callbacks.registerCallback("bytes", [this](String const& path) -> String { callbacks.registerCallback("bytes", [this](String const& path) -> String {
auto assetBytes = bytes(path); auto assetBytes = bytes(path);
@ -204,6 +206,24 @@ Assets::Assets(Settings settings, StringList assetSources) {
auto targetPatchFile = filename.substr(0, filename.size() - strlen(AssetsLuaPatchSuffix)); auto targetPatchFile = filename.substr(0, filename.size() - strlen(AssetsLuaPatchSuffix));
if (auto p = m_files.ptr(targetPatchFile)) if (auto p = m_files.ptr(targetPatchFile))
p->patchSources.append({filename, source}); p->patchSources.append({filename, source});
} else if (filename.endsWith(AssetsPatchListSuffix, String::CaseInsensitive)) {
auto stream = source->read(filename);
size_t patchIndex = 0;
for (auto const& patchPair : inputUtf8Json(stream.begin(), stream.end(), JsonParseType::Top).iterateArray()) {
auto patches = patchPair.getArray("patches");
for (auto& path : patchPair.getArray("paths")) {
if (auto p = m_files.ptr(path.toString())) {
for (size_t i = 0; i != patches.size(); ++i) {
auto& patch = patches[i];
if (patch.isType(Json::Type::String))
p->patchSources.append({patch.toString(), source});
else
p->patchSources.append({strf("{}:[{}].patches[{}]", filename, patchIndex, i), source});
}
}
}
patchIndex++;
}
} else { } else {
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
if (filename.endsWith(AssetsPatchSuffix + toString(i), String::CaseInsensitive)) { if (filename.endsWith(AssetsPatchSuffix + toString(i), String::CaseInsensitive)) {
@ -289,7 +309,7 @@ Assets::Assets(Settings settings, StringList assetSources) {
digest.push(assetPath); digest.push(assetPath);
digest.push(DataStreamBuffer::serialize(descriptor.source->open(descriptor.sourceName)->size())); digest.push(DataStreamBuffer::serialize(descriptor.source->open(descriptor.sourceName)->size()));
for (auto const& pair : descriptor.patchSources) for (auto const& pair : descriptor.patchSources)
digest.push(DataStreamBuffer::serialize(pair.second->open(pair.first)->size())); digest.push(DataStreamBuffer::serialize(pair.second->open(AssetPath::removeSubPath(pair.first))->size()));
} }
} }
@ -956,32 +976,35 @@ Json Assets::readJson(String const& path) const {
try { try {
Json result = inputUtf8Json(streamData.begin(), streamData.end(), JsonParseType::Top); Json result = inputUtf8Json(streamData.begin(), streamData.end(), JsonParseType::Top);
for (auto const& pair : m_files.get(path).patchSources) { for (auto const& pair : m_files.get(path).patchSources) {
auto& patchPath = pair.first; auto patchAssetPath = AssetPath::split(pair.first);
auto& patchBasePath = patchAssetPath.basePath;
auto& patchSource = pair.second; auto& patchSource = pair.second;
auto patchStream = patchSource->read(patchPath); auto patchStream = patchSource->read(patchBasePath);
if (patchPath.endsWith(".lua")) { if (patchBasePath.endsWith(".lua")) {
RecursiveMutexLocker luaLocker(m_luaMutex); RecursiveMutexLocker luaLocker(m_luaMutex);
// Kae: i don't like that lock. perhaps have a LuaEngine and patch context cache per worker thread later on? // Kae: i don't like that lock. perhaps have a LuaEngine and patch context cache per worker thread later on?
LuaContextPtr& context = m_patchContexts[patchPath]; LuaContextPtr& context = m_patchContexts[patchBasePath];
if (!context) { if (!context) {
context = make_shared<LuaContext>(as<LuaEngine>(m_luaEngine.get())->createContext()); context = make_shared<LuaContext>(as<LuaEngine>(m_luaEngine.get())->createContext());
context->load(patchStream, patchPath); context->load(patchStream, patchBasePath);
} }
auto newResult = context->invokePath<Json>("patch", result, path); auto newResult = context->invokePath<Json>("patch", result, path);
if (newResult) if (newResult)
result = std::move(newResult); result = std::move(newResult);
} else { } else {
auto patchJson = inputUtf8Json(patchStream.begin(), patchStream.end(), JsonParseType::Top); auto patchJson = inputUtf8Json(patchStream.begin(), patchStream.end(), JsonParseType::Top);
if (patchAssetPath.subPath)
patchJson = patchJson.query(*patchAssetPath.subPath);
if (patchJson.isType(Json::Type::Array)) { if (patchJson.isType(Json::Type::Array)) {
auto patchData = patchJson.toArray(); auto patchData = patchJson.toArray();
try { try {
result = checkPatchArray(patchPath, patchSource, result, patchData, {}); result = checkPatchArray(pair.first, patchSource, result, patchData, {});
} catch (JsonPatchTestFail const& e) { } catch (JsonPatchTestFail const& e) {
Logger::debug("Patch test failure from file {} in source: '{}' at '{}'. Caused by: {}", patchPath, patchSource->metadata().value("name", ""), m_assetSourcePaths.getLeft(patchSource), e.what()); Logger::debug("Patch test failure from file {} in source: '{}' at '{}'. Caused by: {}", pair.first, patchSource->metadata().value("name", ""), m_assetSourcePaths.getLeft(patchSource), e.what());
} catch (JsonPatchException const& e) { } catch (JsonPatchException const& e) {
Logger::error("Could not apply patch from file {} in source: '{}' at '{}'. Caused by: {}", patchPath, patchSource->metadata().value("name", ""), m_assetSourcePaths.getLeft(patchSource), e.what()); Logger::error("Could not apply patch from file {} in source: '{}' at '{}'. Caused by: {}", pair.first, patchSource->metadata().value("name", ""), m_assetSourcePaths.getLeft(patchSource), e.what());
} }
} else if (patchJson.isType(Json::Type::Object)) {//Kae: Do a good ol' json merge instead if the .patch file is a Json object } else if (patchJson.isType(Json::Type::Object)) {
result = jsonMergeNulling(result, patchJson.toObject()); result = jsonMergeNulling(result, patchJson.toObject());
} }
} }
@ -1219,7 +1242,7 @@ shared_ptr<Assets::AssetData> Assets::loadImage(AssetPath const& path) const {
shared_ptr<Assets::AssetData> Assets::loadAudio(AssetPath const& path) const { shared_ptr<Assets::AssetData> Assets::loadAudio(AssetPath const& path) const {
return unlockDuring([&]() { return unlockDuring([&]() {
auto newData = make_shared<AudioData>(); auto newData = make_shared<AudioData>();
newData->audio = make_shared<Audio>(open(path.basePath)); newData->audio = make_shared<Audio>(open(path.basePath), path.basePath);
newData->needsPostProcessing = newData->audio->compressed(); newData->needsPostProcessing = newData->audio->compressed();
return newData; return newData;
}); });

View File

@ -64,12 +64,12 @@ void CellularLightArray<ScalarLightTraits>::calculatePointLighting(size_t xmin,
attenuation += min(blockAttenuation, circularizedPerBlockObstacleAttenuation) * m_pointObstacleBoost; attenuation += min(blockAttenuation, circularizedPerBlockObstacleAttenuation) * m_pointObstacleBoost;
if (attenuation < 1.0f) { if (attenuation < 1.0f) {
auto newLight = ScalarLightTraits::subtract(light.value, attenuation); if (m_pointAdditive) {
if (ScalarLightTraits::maxIntensity(newLight) > 0.0001f) { auto newLight = ScalarLightTraits::subtract(light.value, attenuation);
if (light.asSpread) if (ScalarLightTraits::maxIntensity(newLight) > 0.0001f)
setLight(x, y, lvalue + newLight * 0.15f); setLight(x, y, lvalue + (light.asSpread ? newLight * 0.15f : newLight));
else } else {
setLight(x, y, lvalue + newLight); setLight(x, y, ScalarLightTraits::max(ScalarLightTraits::subtract(light.value, attenuation), lvalue));
} }
} }
} }
@ -137,12 +137,12 @@ void CellularLightArray<ColoredLightTraits>::calculatePointLighting(size_t xmin,
attenuation += min(blockAttenuation, circularizedPerBlockObstacleAttenuation) * m_pointObstacleBoost; attenuation += min(blockAttenuation, circularizedPerBlockObstacleAttenuation) * m_pointObstacleBoost;
if (attenuation < 1.0f) { if (attenuation < 1.0f) {
auto newLight = ColoredLightTraits::subtract(light.value, attenuation); if (m_pointAdditive) {
if (ColoredLightTraits::maxIntensity(newLight) > 0.0001f) { auto newLight = ColoredLightTraits::subtract(light.value, attenuation);
if (light.asSpread) if (ColoredLightTraits::maxIntensity(newLight) > 0.0001f)
setLight(x, y, lvalue + newLight * 0.15f); setLight(x, y, lvalue + (light.asSpread ? newLight * 0.15f : newLight));
else } else {
setLight(x, y, lvalue + newLight); setLight(x, y, ColoredLightTraits::max(ColoredLightTraits::subtract(light.value, attenuation), lvalue));
} }
} }
} }

View File

@ -60,7 +60,7 @@ public:
}; };
void setParameters(unsigned spreadPasses, float spreadMaxAir, float spreadMaxObstacle, void setParameters(unsigned spreadPasses, float spreadMaxAir, float spreadMaxObstacle,
float pointMaxAir, float pointMaxObstacle, float pointObstacleBoost); float pointMaxAir, float pointMaxObstacle, float pointObstacleBoost, bool pointAdditive);
// The border around the target lighting array where initial lighting / light // The border around the target lighting array where initial lighting / light
// source data is required. Based on parameters. // source data is required. Based on parameters.
@ -129,6 +129,7 @@ private:
float m_pointMaxAir; float m_pointMaxAir;
float m_pointMaxObstacle; float m_pointMaxObstacle;
float m_pointObstacleBoost; float m_pointObstacleBoost;
bool m_pointAdditive;
}; };
typedef CellularLightArray<ColoredLightTraits> ColoredCellularLightArray; typedef CellularLightArray<ColoredLightTraits> ColoredCellularLightArray;
@ -204,13 +205,14 @@ inline Vec3F ColoredLightTraits::max(Vec3F const& v1, Vec3F const& v2) {
template <typename LightTraits> template <typename LightTraits>
void CellularLightArray<LightTraits>::setParameters(unsigned spreadPasses, float spreadMaxAir, float spreadMaxObstacle, void CellularLightArray<LightTraits>::setParameters(unsigned spreadPasses, float spreadMaxAir, float spreadMaxObstacle,
float pointMaxAir, float pointMaxObstacle, float pointObstacleBoost) { float pointMaxAir, float pointMaxObstacle, float pointObstacleBoost, bool pointAdditive) {
m_spreadPasses = spreadPasses; m_spreadPasses = spreadPasses;
m_spreadMaxAir = spreadMaxAir; m_spreadMaxAir = spreadMaxAir;
m_spreadMaxObstacle = spreadMaxObstacle; m_spreadMaxObstacle = spreadMaxObstacle;
m_pointMaxAir = pointMaxAir; m_pointMaxAir = pointMaxAir;
m_pointMaxObstacle = pointMaxObstacle; m_pointMaxObstacle = pointMaxObstacle;
m_pointObstacleBoost = pointObstacleBoost; m_pointObstacleBoost = pointObstacleBoost;
m_pointAdditive = pointAdditive;
} }
template <typename LightTraits> template <typename LightTraits>

View File

@ -73,7 +73,8 @@ void CellularLightingCalculator::setParameters(Json const& config) {
config.getFloat("spreadMaxObstacle"), config.getFloat("spreadMaxObstacle"),
config.getFloat("pointMaxAir"), config.getFloat("pointMaxAir"),
config.getFloat("pointMaxObstacle"), config.getFloat("pointMaxObstacle"),
config.getFloat("pointObstacleBoost") config.getFloat("pointObstacleBoost"),
config.getBool("pointAdditive", false)
); );
else else
m_lightArray.left().setParameters( m_lightArray.left().setParameters(
@ -82,7 +83,8 @@ void CellularLightingCalculator::setParameters(Json const& config) {
config.getFloat("spreadMaxObstacle"), config.getFloat("spreadMaxObstacle"),
config.getFloat("pointMaxAir"), config.getFloat("pointMaxAir"),
config.getFloat("pointMaxObstacle"), config.getFloat("pointMaxObstacle"),
config.getFloat("pointObstacleBoost") config.getFloat("pointObstacleBoost"),
config.getBool("pointAdditive", false)
); );
} }
@ -190,7 +192,8 @@ void CellularLightIntensityCalculator::setParameters(Json const& config) {
config.getFloat("spreadMaxObstacle"), config.getFloat("spreadMaxObstacle"),
config.getFloat("pointMaxAir"), config.getFloat("pointMaxAir"),
config.getFloat("pointMaxObstacle"), config.getFloat("pointMaxObstacle"),
config.getFloat("pointObstacleBoost") config.getFloat("pointObstacleBoost"),
config.getBool("pointAdditive", false)
); );
} }

View File

@ -310,49 +310,54 @@ void Mixer::read(int16_t* outBuffer, size_t frameCount, ExtraMixFunction extraMi
m_mixBuffer[i] = 0; m_mixBuffer[i] = 0;
ramt += silentSamples * channels; ramt += silentSamples * channels;
} }
ramt += audioInstance->m_audio.resample(channels, sampleRate, m_mixBuffer.ptr() + ramt, bufferSize - ramt, pitchMultiplier); try {
while (ramt != bufferSize && !finished) { ramt += audioInstance->m_audio.resample(channels, sampleRate, m_mixBuffer.ptr() + ramt, bufferSize - ramt, pitchMultiplier);
// Only seek back to the beginning and read more data if loops is < 0 while (ramt != bufferSize && !finished) {
// (loop forever), or we have more loops to go, otherwise, the sample is // Only seek back to the beginning and read more data if loops is < 0
// finished. // (loop forever), or we have more loops to go, otherwise, the sample is
if (audioInstance->m_loops != 0) { // finished.
audioInstance->m_audio.seekSample(0); if (audioInstance->m_loops != 0) {
ramt += audioInstance->m_audio.resample(channels, sampleRate, m_mixBuffer.ptr() + ramt, bufferSize - ramt, pitchMultiplier); audioInstance->m_audio.seekSample(0);
if (audioInstance->m_loops > 0) ramt += audioInstance->m_audio.resample(channels, sampleRate, m_mixBuffer.ptr() + ramt, bufferSize - ramt, pitchMultiplier);
--audioInstance->m_loops; if (audioInstance->m_loops > 0)
} else { --audioInstance->m_loops;
finished = true; } else {
} finished = true;
}
if (audioInstance->m_clockStop && *audioInstance->m_clockStop < sampleEndTime) {
for (size_t s = 0; s < ramt / channels; ++s) {
unsigned millisecondsInBuffer = (s * 1000) / sampleRate;
auto sampleTime = sampleStartTime + millisecondsInBuffer;
if (sampleTime > *audioInstance->m_clockStop) {
float volume = 0.0f;
if (audioInstance->m_clockStopFadeOut > 0)
volume = 1.0f - (float)(sampleTime - *audioInstance->m_clockStop) / (float)audioInstance->m_clockStopFadeOut;
if (volume <= 0) {
for (size_t c = 0; c < channels; ++c)
m_mixBuffer[s * channels + c] = 0;
} else {
for (size_t c = 0; c < channels; ++c)
m_mixBuffer[s * channels + c] *= volume;
}
} }
} }
if (sampleEndTime > *audioInstance->m_clockStop + audioInstance->m_clockStopFadeOut) if (audioInstance->m_clockStop && *audioInstance->m_clockStop < sampleEndTime) {
finished = true; for (size_t s = 0; s < ramt / channels; ++s) {
} unsigned millisecondsInBuffer = (s * 1000) / sampleRate;
auto sampleTime = sampleStartTime + millisecondsInBuffer;
if (sampleTime > *audioInstance->m_clockStop) {
float volume = 0.0f;
if (audioInstance->m_clockStopFadeOut > 0)
volume = 1.0f - (float)(sampleTime - *audioInstance->m_clockStop) / (float)audioInstance->m_clockStopFadeOut;
for (size_t s = 0; s < ramt / channels; ++s) { if (volume <= 0) {
float vol = lerp((float)s / frameCount, beginVolume * groupVolume * audioStopVolBegin, endVolume * groupEndVolume * audioStopVolEnd); for (size_t c = 0; c < channels; ++c)
for (size_t c = 0; c < channels; ++c) { m_mixBuffer[s * channels + c] = 0;
float sample = m_mixBuffer[s * channels + c] * vol * audioState.positionalChannelVolumes[c] * audioInstance->m_volume.value; } else {
int16_t& outSample = outBuffer[s * channels + c]; for (size_t c = 0; c < channels; ++c)
outSample = clamp(sample + outSample, -32767.0f, 32767.0f); m_mixBuffer[s * channels + c] *= volume;
}
}
}
if (sampleEndTime > *audioInstance->m_clockStop + audioInstance->m_clockStopFadeOut)
finished = true;
} }
for (size_t s = 0; s < ramt / channels; ++s) {
float vol = lerp((float)s / frameCount, beginVolume * groupVolume * audioStopVolBegin, endVolume * groupEndVolume * audioStopVolEnd);
for (size_t c = 0; c < channels; ++c) {
float sample = m_mixBuffer[s * channels + c] * vol * audioState.positionalChannelVolumes[c] * audioInstance->m_volume.value;
int16_t& outSample = outBuffer[s * channels + c];
outSample = clamp(sample + outSample, -32767.0f, 32767.0f);
}
}
} catch (Star::AudioException const& e) {
Logger::error("Error reading audio '{}': {}", audioInstance->m_audio.name(), e.what());
finished = true;
} }
audioInstance->m_volume.value = audioStopVolEnd; audioInstance->m_volume.value = audioStopVolEnd;

View File

@ -301,20 +301,19 @@ void ClientApplication::processInput(InputEvent const& event) {
m_controllerRightStick[1] = cAxis->controllerAxisValue; m_controllerRightStick[1] = cAxis->controllerAxisValue;
} }
if (!m_errorScreen->accepted() && m_errorScreen->handleInputEvent(event)) bool processed = !m_errorScreen->accepted() && m_errorScreen->handleInputEvent(event);
return;
bool processed = false; if (!processed) {
if (m_state == MainAppState::Splash) {
processed = m_cinematicOverlay->handleInputEvent(event);
} else if (m_state == MainAppState::Title) {
if (!(processed = m_cinematicOverlay->handleInputEvent(event)))
processed = m_titleScreen->handleInputEvent(event);
if (m_state == MainAppState::Splash) { } else if (m_state == MainAppState::SinglePlayer || m_state == MainAppState::MultiPlayer) {
processed = m_cinematicOverlay->handleInputEvent(event); if (!(processed = m_cinematicOverlay->handleInputEvent(event)))
} else if (m_state == MainAppState::Title) { processed = m_mainInterface->handleInputEvent(event);
if (!(processed = m_cinematicOverlay->handleInputEvent(event))) }
processed = m_titleScreen->handleInputEvent(event);
} else if (m_state == MainAppState::SinglePlayer || m_state == MainAppState::MultiPlayer) {
if (!(processed = m_cinematicOverlay->handleInputEvent(event)))
processed = m_mainInterface->handleInputEvent(event);
} }
m_input->handleInput(event, processed); m_input->handleInput(event, processed);
@ -361,7 +360,7 @@ void ClientApplication::update() {
m_guiContext->cleanup(); m_guiContext->cleanup();
m_edgeKeyEvents.clear(); m_edgeKeyEvents.clear();
m_input->reset(); m_input->update();
} }
void ClientApplication::render() { void ClientApplication::render() {
@ -401,6 +400,17 @@ void ClientApplication::render() {
}); });
LogMap::set("client_render_world_painter", strf(u8"{:05d}\u00b5s", Time::monotonicMicroseconds() - paintStart)); LogMap::set("client_render_world_painter", strf(u8"{:05d}\u00b5s", Time::monotonicMicroseconds() - paintStart));
LogMap::set("client_render_world_total", strf(u8"{:05d}\u00b5s", Time::monotonicMicroseconds() - totalStart)); LogMap::set("client_render_world_total", strf(u8"{:05d}\u00b5s", Time::monotonicMicroseconds() - totalStart));
auto size = Vec2F(renderer->screenSize());
auto quad = renderFlatRect(RectF::withSize(size / -2, size), Vec4B::filled(0), 0.0f);
for (auto& layer : m_postProcessLayers) {
for (unsigned i = 0; i < layer.passes; i++) {
for (auto& effect : layer.effects) {
renderer->switchEffectConfig(effect);
renderer->render(quad);
}
}
}
} }
renderer->switchEffectConfig("interface"); renderer->switchEffectConfig("interface");
auto start = Time::monotonicMicroseconds(); auto start = Time::monotonicMicroseconds();
@ -452,6 +462,16 @@ void ClientApplication::renderReload() {
renderer->loadConfig(assets->json("/rendering/opengl.config")); renderer->loadConfig(assets->json("/rendering/opengl.config"));
loadEffectConfig("world"); loadEffectConfig("world");
m_postProcessLayers.clear();
auto postProcessLayers = assets->json("/client.config:postProcessLayers").toArray();
for (auto& layer : postProcessLayers) {
auto effects = jsonToStringList(layer.getArray("effects"));
for (auto& effect : effects)
loadEffectConfig(effect);
m_postProcessLayers.append(PostProcessLayer{ std::move(effects), (unsigned)layer.getUInt("passes", 1) });
}
loadEffectConfig("interface"); loadEffectConfig("interface");
} }
@ -585,6 +605,7 @@ void ClientApplication::changeState(MainAppState newState) {
m_mainMixer->setUniverseClient(m_universeClient); m_mainMixer->setUniverseClient(m_universeClient);
m_universeClient->setMainPlayer(m_player); m_universeClient->setMainPlayer(m_player);
m_cinematicOverlay->setPlayer(m_player); m_cinematicOverlay->setPlayer(m_player);
m_timeSinceJoin = (int64_t)Time::millisecondsSinceEpoch() / 1000;
auto assets = m_root->assets(); auto assets = m_root->assets();
String loadingCinematic = assets->json("/client.config:loadingCinematic").toString(); String loadingCinematic = assets->json("/client.config:loadingCinematic").toString();
@ -686,10 +707,11 @@ void ClientApplication::setError(String const& error, std::exception const& e) {
void ClientApplication::updateMods(float dt) { void ClientApplication::updateMods(float dt) {
m_cinematicOverlay->update(dt); m_cinematicOverlay->update(dt);
auto ugcService = appController()->userGeneratedContentService(); auto ugcService = appController()->userGeneratedContentService();
if (ugcService) { if (ugcService && m_root->settings().includeUGC) {
Logger::info("Checking for user generated content...");
if (ugcService->triggerContentDownload()) { if (ugcService->triggerContentDownload()) {
StringList modDirectories; StringList modDirectories;
for (auto contentId : ugcService->subscribedContentIds()) { for (auto& contentId : ugcService->subscribedContentIds()) {
if (auto contentDirectory = ugcService->contentDownloadDirectory(contentId)) { if (auto contentDirectory = ugcService->contentDownloadDirectory(contentId)) {
Logger::info("Loading mods from user generated content with id '{}' from directory '{}'", contentId, *contentDirectory); Logger::info("Loading mods from user generated content with id '{}' from directory '{}'", contentId, *contentDirectory);
modDirectories.append(*contentDirectory); modDirectories.append(*contentDirectory);
@ -748,8 +770,36 @@ void ClientApplication::updateTitle(float dt) {
appController()->setAcceptingTextInput(m_titleScreen->textInputActive()); appController()->setAcceptingTextInput(m_titleScreen->textInputActive());
auto p2pNetworkingService = appController()->p2pNetworkingService(); auto p2pNetworkingService = appController()->p2pNetworkingService();
if (p2pNetworkingService) if (p2pNetworkingService) {
p2pNetworkingService->setActivityData("In Main Menu", {}); auto getStateString = [](TitleState state) -> const char* {
switch (state) {
case TitleState::Main:
return "In Main Menu";
case TitleState::Options:
return "In Options";
case TitleState::Mods:
return "In Mods";
case TitleState::SinglePlayerSelectCharacter:
return "Selecting a character for singleplayer";
case TitleState::SinglePlayerCreateCharacter:
return "Creating a character for singleplayer";
case TitleState::MultiPlayerSelectCharacter:
return "Selecting a character for multiplayer";
case TitleState::MultiPlayerCreateCharacter:
return "Creating a character for multiplayer";
case TitleState::MultiPlayerConnect:
return "Awaiting multiplayer connection info";
case TitleState::StartSinglePlayer:
return "Loading Singleplayer";
case TitleState::StartMultiPlayer:
return "Connecting to Multiplayer";
default:
return "";
}
};
p2pNetworkingService->setActivityData("Not In Game", getStateString(m_titleScreen->currentState()), 0, {});
}
if (m_titleScreen->currentState() == TitleState::StartSinglePlayer) { if (m_titleScreen->currentState() == TitleState::StartSinglePlayer) {
changeState(MainAppState::SinglePlayer); changeState(MainAppState::SinglePlayer);
@ -791,6 +841,7 @@ void ClientApplication::updateTitle(float dt) {
void ClientApplication::updateRunning(float dt) { void ClientApplication::updateRunning(float dt) {
try { try {
auto worldClient = m_universeClient->worldClient();
auto p2pNetworkingService = appController()->p2pNetworkingService(); auto p2pNetworkingService = appController()->p2pNetworkingService();
bool clientIPJoinable = m_root->configuration()->get("clientIPJoinable").toBool(); bool clientIPJoinable = m_root->configuration()->get("clientIPJoinable").toBool();
bool clientP2PJoinable = m_root->configuration()->get("clientP2PJoinable").toBool(); bool clientP2PJoinable = m_root->configuration()->get("clientP2PJoinable").toBool();
@ -817,8 +868,54 @@ void ClientApplication::updateRunning(float dt) {
} }
} }
if (p2pNetworkingService) if (p2pNetworkingService) {
p2pNetworkingService->setActivityData("In Game", party); auto getActivityDetail = [&](String const& tag) -> String {
if (tag == "playerName")
return Text::stripEscapeCodes(m_player->name());
if (tag == "playerHealth")
return toString(m_player->health());
if (tag == "playerMaxHealth")
return toString(m_player->maxHealth());
if (tag == "playerEnergy")
return toString(m_player->energy());
if (tag == "playerMaxEnergy")
return toString(m_player->maxEnergy());
if (tag == "playerBreath")
return toString(m_player->breath());
if (tag == "playerMaxBreath")
return toString(m_player->maxBreath());
if (tag == "playerXPos")
return toString(round(m_player->position().x()));
if (tag == "playerYPos")
return toString(round(m_player->position().y()));
if (tag == "worldName") {
if (m_universeClient->clientContext()->playerWorldId().is<ClientShipWorldId>())
return "Player Ship";
else if (WorldTemplate const* worldTemplate = worldClient ? worldClient->currentTemplate().get() : nullptr) {
auto worldName = worldTemplate->worldName();
if (worldName.empty())
return "In World";
else
return Text::stripEscapeCodes(worldName);
}
else
return "Nowhere";
}
return "";
};
String finalDetails = "";
Json activityDetails = m_root->configuration()->getPath("discord.activityDetails");
if (activityDetails.isType(Json::Type::Array)) {
StringList detailsList;
for (auto& detail : activityDetails.iterateArray())
detailsList.append(getActivityDetail(*detail.stringPtr()));
finalDetails = detailsList.join("\n");
} else if (activityDetails.isType(Json::Type::String))
finalDetails = activityDetails.toString().lookupTags(getActivityDetail);
p2pNetworkingService->setActivityData("In Game", finalDetails.utf8Ptr(), m_timeSinceJoin, party);
}
if (!m_mainInterface->inputFocus() && !m_cinematicOverlay->suppressInput()) { if (!m_mainInterface->inputFocus() && !m_cinematicOverlay->suppressInput()) {
m_player->setShifting(isActionTaken(InterfaceAction::PlayerShifting)); m_player->setShifting(isActionTaken(InterfaceAction::PlayerShifting));
@ -896,7 +993,7 @@ void ClientApplication::updateRunning(float dt) {
config->set("zoomLevel", newZoom); config->set("zoomLevel", newZoom);
} }
if (m_controllerLeftStick.magnitudeSquared() > 0.001f) if (m_controllerLeftStick.magnitudeSquared() > 0.01f)
m_player->setMoveVector(m_controllerLeftStick); m_player->setMoveVector(m_controllerLeftStick);
else else
m_player->setMoveVector(Vec2F()); m_player->setMoveVector(Vec2F());
@ -932,7 +1029,7 @@ void ClientApplication::updateRunning(float dt) {
if (checkDisconnection()) if (checkDisconnection())
return; return;
if (auto worldClient = m_universeClient->worldClient()) { if (worldClient) {
m_worldPainter->update(dt); m_worldPainter->update(dt);
auto& broadcastCallback = worldClient->broadcastCallback(); auto& broadcastCallback = worldClient->broadcastCallback();
if (!broadcastCallback) { if (!broadcastCallback) {

View File

@ -53,6 +53,11 @@ private:
String password; String password;
}; };
struct PostProcessLayer {
List<String> effects;
unsigned passes;
};
void renderReload(); void renderReload();
void changeState(MainAppState newState); void changeState(MainAppState newState);
@ -99,6 +104,8 @@ private:
WorldRenderData m_renderData; WorldRenderData m_renderData;
MainInterfacePtr m_mainInterface; MainInterfacePtr m_mainInterface;
List<PostProcessLayer> m_postProcessLayers;
// Valid if main app state == SinglePlayer // Valid if main app state == SinglePlayer
UniverseServerPtr m_universeServer; UniverseServerPtr m_universeServer;
@ -108,7 +115,7 @@ private:
int m_cameraOffsetDownTicks = 0; int m_cameraOffsetDownTicks = 0;
Vec2F m_cameraPositionSmoother; Vec2F m_cameraPositionSmoother;
Vec2F m_cameraSmoothDelta; Vec2F m_cameraSmoothDelta;
int m_cameraZoomDirection; int m_cameraZoomDirection = 0;
int m_minInterfaceScale = 2; int m_minInterfaceScale = 2;
int m_maxInterfaceScale = 3; int m_maxInterfaceScale = 3;
@ -121,6 +128,7 @@ private:
Maybe<PendingMultiPlayerConnection> m_pendingMultiPlayerConnection; Maybe<PendingMultiPlayerConnection> m_pendingMultiPlayerConnection;
Maybe<HostAddressWithPort> m_currentRemoteJoin; Maybe<HostAddressWithPort> m_currentRemoteJoin;
int64_t m_timeSinceJoin = 0;
}; };
} }

View File

@ -239,17 +239,19 @@ public:
size_t readPartial(int16_t* buffer, size_t bufferSize) { size_t readPartial(int16_t* buffer, size_t bufferSize) {
int bitstream; int bitstream;
int read; int read = OV_HOLE;
// ov_read takes int parameter, so do some magic here to make sure we don't // ov_read takes int parameter, so do some magic here to make sure we don't
// overflow // overflow
bufferSize *= 2; bufferSize *= 2;
do {
#if STAR_LITTLE_ENDIAN #if STAR_LITTLE_ENDIAN
read = ov_read(&m_vorbisFile, (char*)buffer, bufferSize, 0, 2, 1, &bitstream); read = ov_read(&m_vorbisFile, (char*)buffer, bufferSize, 0, 2, 1, &bitstream);
#else #else
read = ov_read(&m_vorbisFile, (char*)buffer, bufferSize, 1, 2, 1, &bitstream); read = ov_read(&m_vorbisFile, (char*)buffer, bufferSize, 1, 2, 1, &bitstream);
#endif #endif
} while (read == OV_HOLE);
if (read < 0) if (read < 0)
throw AudioException("Error in Audio::read"); throw AudioException::format("Error in Audio::read ({})", read);
// read in bytes, returning number of int16_t samples. // read in bytes, returning number of int16_t samples.
return read / 2; return read / 2;
@ -349,7 +351,8 @@ private:
ExternalBuffer m_memoryFile; ExternalBuffer m_memoryFile;
}; };
Audio::Audio(IODevicePtr device) { Audio::Audio(IODevicePtr device, String name) {
m_name = name;
if (!device->isOpen()) if (!device->isOpen())
device->open(IOMode::Read); device->open(IOMode::Read);
@ -579,4 +582,12 @@ size_t Audio::resample(unsigned destinationChannels, unsigned destinationSampleR
} }
} }
String const& Audio::name() const {
return m_name;
}
void Audio::setName(String name) {
m_name = std::move(name);
}
} }

View File

@ -26,7 +26,7 @@ STAR_EXCEPTION(AudioException, StarException);
// instances is not expensive. // instances is not expensive.
class Audio { class Audio {
public: public:
explicit Audio(IODevicePtr device); explicit Audio(IODevicePtr device, String name = "");
Audio(Audio const& audio); Audio(Audio const& audio);
Audio(Audio&& audio); Audio(Audio&& audio);
@ -90,12 +90,16 @@ public:
int16_t* destinationBuffer, size_t destinationBufferSize, int16_t* destinationBuffer, size_t destinationBufferSize,
double velocity = 1.0); double velocity = 1.0);
String const& name() const;
void setName(String name);
private: private:
// If audio is uncompressed, this will be null. // If audio is uncompressed, this will be null.
CompressedAudioImplPtr m_compressed; CompressedAudioImplPtr m_compressed;
UncompressedAudioImplPtr m_uncompressed; UncompressedAudioImplPtr m_uncompressed;
ByteArray m_workingBuffer; ByteArray m_workingBuffer;
String m_name;
}; };
} }

View File

@ -196,6 +196,11 @@ void BTreeDatabase::forAll(function<void(ByteArray, ByteArray)> v) {
m_impl.forAll(std::move(v)); m_impl.forAll(std::move(v));
} }
void BTreeDatabase::recoverAll(function<void(ByteArray, ByteArray)> v, function<void(String const&, std::exception const&)> e) {
ReadLocker readLocker(m_lock);
m_impl.recoverAll(std::move(v), std::move(e));
}
bool BTreeDatabase::insert(ByteArray const& k, ByteArray const& data) { bool BTreeDatabase::insert(ByteArray const& k, ByteArray const& data) {
WriteLocker writeLocker(m_lock); WriteLocker writeLocker(m_lock);
checkKeySize(k); checkKeySize(k);

View File

@ -64,6 +64,7 @@ public:
void forEach(ByteArray const& lower, ByteArray const& upper, function<void(ByteArray, ByteArray)> v); void forEach(ByteArray const& lower, ByteArray const& upper, function<void(ByteArray, ByteArray)> v);
void forAll(function<void(ByteArray, ByteArray)> v); void forAll(function<void(ByteArray, ByteArray)> v);
void recoverAll(function<void(ByteArray, ByteArray)> v, function<void(String const&, std::exception const&)> e);
// Returns true if a value was overwritten // Returns true if a value was overwritten
bool insert(ByteArray const& k, ByteArray const& data); bool insert(ByteArray const& k, ByteArray const& data);

View File

@ -10,6 +10,8 @@ namespace Star {
struct WindowsSymInitializer { struct WindowsSymInitializer {
WindowsSymInitializer() { WindowsSymInitializer() {
DWORD options = SymGetOptions();
SymSetOptions(options | SYMOPT_LOAD_LINES);
if (!SymInitialize(GetCurrentProcess(), NULL, TRUE)) if (!SymInitialize(GetCurrentProcess(), NULL, TRUE))
fatalError("SymInitialize failed", false); fatalError("SymInitialize failed", false);
} }
@ -126,9 +128,19 @@ OutputProxy outputStack(StackCapture stack) {
symbol->SizeOfStruct = sizeof(SYMBOL_INFO); symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
symbol->MaxNameLen = MAX_SYM_NAME; symbol->MaxNameLen = MAX_SYM_NAME;
DWORD64 displacement = 0; format(os, "[{:0{}}] {}", i, (int)log10(stack.second) + 1, (void*)stack.first[i]);
format(os, "[{}] {}", i, (void*)stack.first[i]); IMAGEHLP_LINE64 line{};
if (SymFromAddr(process, stack.first[i], &displacement, symbol)) DWORD displacement = 0;
line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
if (SymGetLineFromAddr64(process, stack.first[i], &displacement, &line) && *line.FileName) {
char* file = line.FileName;
for (char* i = file; *i; ++i) {
if (*i == '\\' || *i == '/')
file = i;
}
format(os, " ({}:{})", ++file, line.LineNumber);
}
if (SymFromAddr(process, stack.first[i], NULL, symbol))
format(os, " {}", symbol->Name); format(os, " {}", symbol->Name);
if (i + 1 < stack.second) if (i + 1 < stack.second)

View File

@ -73,23 +73,23 @@ EnumMap<Key> const KeyNames{
{Key::Y, "Y"}, {Key::Y, "Y"},
{Key::Z, "Z"}, {Key::Z, "Z"},
{Key::Delete, "Del"}, {Key::Delete, "Del"},
{Key::Kp0, "Kp0"}, {Key::Keypad0, "Keypad 0"},
{Key::Kp1, "Kp1"}, {Key::Keypad1, "Keypad 1"},
{Key::Kp2, "Kp2"}, {Key::Keypad2, "Keypad 2"},
{Key::Kp3, "Kp3"}, {Key::Keypad3, "Keypad 3"},
{Key::Kp4, "Kp4"}, {Key::Keypad4, "Keypad 4"},
{Key::Kp5, "Kp5"}, {Key::Keypad5, "Keypad 5"},
{Key::Kp6, "Kp6"}, {Key::Keypad6, "Keypad 6"},
{Key::Kp7, "Kp7"}, {Key::Keypad7, "Keypad 7"},
{Key::Kp8, "Kp8"}, {Key::Keypad8, "Keypad 8"},
{Key::Kp9, "Kp9"}, {Key::Keypad9, "Keypad 9"},
{Key::Kp_period, "Kp_period"}, {Key::KeypadPeriod, "Keypad ."},
{Key::Kp_divide, "Kp_divide"}, {Key::KeypadDivide, "Keypad /"},
{Key::Kp_multiply, "Kp_multiply"}, {Key::KeypadMultiply, "Keypad *"},
{Key::Kp_minus, "Kp_minus"}, {Key::KeypadMinus, "Keypad -"},
{Key::Kp_plus, "Kp_plus"}, {Key::KeypadPlus, "Keypad +"},
{Key::Kp_enter, "Kp_enter"}, {Key::KeypadEnter, "Keypad Enter"},
{Key::Kp_equals, "Kp_equals"}, {Key::KeypadEquals, "Keypad ="},
{Key::Up, "Up"}, {Key::Up, "Up"},
{Key::Down, "Down"}, {Key::Down, "Down"},
{Key::Right, "Right"}, {Key::Right, "Right"},
@ -114,6 +114,15 @@ EnumMap<Key> const KeyNames{
{Key::F13, "F13"}, {Key::F13, "F13"},
{Key::F14, "F14"}, {Key::F14, "F14"},
{Key::F15, "F15"}, {Key::F15, "F15"},
{Key::F16, "F16"},
{Key::F17, "F17"},
{Key::F18, "F18"},
{Key::F19, "F19"},
{Key::F20, "F20"},
{Key::F21, "F21"},
{Key::F22, "F22"},
{Key::F23, "F23"},
{Key::F24, "F24"},
{Key::NumLock, "NumLock"}, {Key::NumLock, "NumLock"},
{Key::CapsLock, "CapsLock"}, {Key::CapsLock, "CapsLock"},
{Key::ScrollLock, "ScrollLock"}, {Key::ScrollLock, "ScrollLock"},

View File

@ -78,23 +78,23 @@ enum class Key : uint16_t {
Y, Y,
Z, Z,
Delete, Delete,
Kp0, Keypad0,
Kp1, Keypad1,
Kp2, Keypad2,
Kp3, Keypad3,
Kp4, Keypad4,
Kp5, Keypad5,
Kp6, Keypad6,
Kp7, Keypad7,
Kp8, Keypad8,
Kp9, Keypad9,
Kp_period, KeypadPeriod,
Kp_divide, KeypadDivide,
Kp_multiply, KeypadMultiply,
Kp_minus, KeypadMinus,
Kp_plus, KeypadPlus,
Kp_enter, KeypadEnter,
Kp_equals, KeypadEquals,
Up, Up,
Down, Down,
Right, Right,
@ -137,7 +137,18 @@ enum class Key : uint16_t {
SysReq, SysReq,
Pause, Pause,
Menu, Menu,
Power Power,
// can't have this where I want because canvases
// pass keycodes to Lua as a numeric code >:[
F16,
F17,
F18,
F19,
F20,
F21,
F22,
F23,
F24
}; };
extern EnumMap<Key> const KeyNames; extern EnumMap<Key> const KeyNames;

View File

@ -89,7 +89,7 @@ void Logger::refreshLoggable() {
shared_ptr<StdoutLogSink> Logger::s_stdoutSink = make_shared<StdoutLogSink>(); shared_ptr<StdoutLogSink> Logger::s_stdoutSink = make_shared<StdoutLogSink>();
HashSet<LogSinkPtr> Logger::s_sinks{s_stdoutSink}; HashSet<LogSinkPtr> Logger::s_sinks{s_stdoutSink};
Array<bool, 4> Logger::s_loggable = Array<bool, 4>::filled(false); Array<bool, 4> Logger::s_loggable = Array<bool, 4>{false, true, true, true};
Mutex Logger::s_mutex; Mutex Logger::s_mutex;
String LogMap::getValue(String const& key) { String LogMap::getValue(String const& key) {

View File

@ -2,6 +2,18 @@
#ifdef STAR_USE_JEMALLOC #ifdef STAR_USE_JEMALLOC
#include "jemalloc/jemalloc.h" #include "jemalloc/jemalloc.h"
#elif STAR_USE_MIMALLOC
#include "mimalloc.h"
#elif STAR_USE_RPMALLOC
#include "rpnew.h"
bool rpm_linker_ref() {
rpmalloc_linker_reference();
return true;
}
static bool _rpm_linker_ref = rpm_linker_ref();
#endif #endif
namespace Star { namespace Star {
@ -42,6 +54,38 @@ namespace Star {
::sdallocx(ptr, size, 0); ::sdallocx(ptr, size, 0);
} }
#endif #endif
#elif STAR_USE_MIMALLOC
void* malloc(size_t size) {
return mi_malloc(size);
}
void* realloc(void* ptr, size_t size) {
return mi_realloc(ptr, size);
}
void free(void* ptr) {
return mi_free(ptr);
}
void free(void* ptr, size_t size) {
return mi_free_size(ptr, size);
}
#elif STAR_USE_RPMALLOC
void* malloc(size_t size) {
return rpmalloc(size);
}
void* realloc(void* ptr, size_t size) {
return rprealloc(ptr, size);
}
void free(void* ptr) {
return rpfree(ptr);
}
void free(void* ptr, size_t) {
return rpfree(ptr);
}
#else #else
void* malloc(size_t size) { void* malloc(size_t size) {
return ::malloc(size); return ::malloc(size);
@ -62,6 +106,9 @@ namespace Star {
} }
#ifndef STAR_USE_RPMALLOC
void* operator new(std::size_t size) { void* operator new(std::size_t size) {
auto ptr = Star::malloc(size); auto ptr = Star::malloc(size);
if (!ptr) if (!ptr)
@ -110,3 +157,5 @@ void operator delete(void* ptr, std::size_t size) noexcept {
void operator delete[](void* ptr, std::size_t size) noexcept { void operator delete[](void* ptr, std::size_t size) noexcept {
Star::free(ptr, size); Star::free(ptr, size);
} }
#endif

View File

@ -4,6 +4,8 @@
#include <winsock2.h> #include <winsock2.h>
#include <ws2tcpip.h> #include <ws2tcpip.h>
#include <stdio.h> #include <stdio.h>
#include "StarString_windows.hpp"
#else #else
#ifdef STAR_SYSTEM_FREEBSD #ifdef STAR_SYSTEM_FREEBSD
#include <sys/types.h> #include <sys/types.h>
@ -42,17 +44,19 @@ static WindowsSocketInitializer g_windowsSocketInitializer;
inline String netErrorString() { inline String netErrorString() {
#ifdef STAR_SYSTEM_WINDOWS #ifdef STAR_SYSTEM_WINDOWS
LPVOID lpMsgBuf = NULL; LPWSTR lpMsgBuf = NULL;
int error = WSAGetLastError();
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM
| FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK,
NULL, NULL,
WSAGetLastError(), error,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
(LPTSTR)&lpMsgBuf, (LPTSTR)&lpMsgBuf,
0, 0,
NULL); NULL);
String result = String((char*)lpMsgBuf); String result = strf("{} - {}", error, utf16ToString(lpMsgBuf));
if (lpMsgBuf != NULL) if (lpMsgBuf != NULL)
LocalFree(lpMsgBuf); LocalFree(lpMsgBuf);

View File

@ -208,8 +208,8 @@ typename Container::value_type Random::randValueFrom(
template <typename Container> template <typename Container>
void Random::shuffle(Container& container) { void Random::shuffle(Container& container) {
size_t max = container.size(); RandomSource random;
std::shuffle(container.begin(), container.end(), URBG<size_t>([max]() { return Random::randUInt(max - 1); })); std::shuffle(container.begin(), container.end(), URBG<size_t>([&]() { return static_cast<size_t>(random.randu64()); }));
} }
} }

View File

@ -134,11 +134,14 @@ private:
template <typename Container, typename T, typename... TL> template <typename Container, typename T, typename... TL>
void staticRandomShuffle(Container& container, T const& d, TL const&... rest) { void staticRandomShuffle(Container& container, T const& d, TL const&... rest) {
int mix = 0; auto begin = container.begin();
size_t max = container.size(); auto end = container.end();
std::shuffle(container.begin(), container.end(), URBG<size_t>([&]() { auto it = begin;
return staticRandomU32Range(0, max - 1, ++mix, d, rest...); for (int i = 1, mix = 0; ++it != end; ++i) {
})); int off = staticRandomU32Range(0, i, ++mix, d, rest...);
if (off != i)
std::swap(*it, *(begin + off));
}
} }
} }

View File

@ -36,6 +36,8 @@ namespace Text {
static auto stripEscapeRegex = std::regex(strf("\\{:c}[^;]*{:c}", CmdEsc, EndEsc)); static auto stripEscapeRegex = std::regex(strf("\\{:c}[^;]*{:c}", CmdEsc, EndEsc));
String stripEscapeCodes(String const& s) { String stripEscapeCodes(String const& s) {
if (s.empty())
return s;
return std::regex_replace(s.utf8(), stripEscapeRegex, ""); return std::regex_replace(s.utf8(), stripEscapeRegex, "");
} }

View File

@ -131,7 +131,18 @@ struct MutexImpl {
} }
void lock() { void lock() {
pthread_mutex_lock(&mutex); #ifndef STAR_SYSTEM_MACOS
timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
ts.tv_sec += 60;
if (pthread_mutex_timedlock(&mutex, &ts) != 0) {
Logger::warn("Mutex lock is taking too long, printing stack");
printStack("Mutex::lock");
#else
{
#endif
pthread_mutex_lock(&mutex);
}
} }
void unlock() { void unlock() {
@ -199,7 +210,18 @@ struct RecursiveMutexImpl {
} }
void lock() { void lock() {
pthread_mutex_lock(&mutex); #ifndef STAR_SYSTEM_MACOS
timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
ts.tv_sec += 60;
if (pthread_mutex_timedlock(&mutex, &ts) != 0) {
Logger::warn("RecursiveMutex lock is taking too long, printing stack");
printStack("RecursiveMutex::lock");
#else
{
#endif
pthread_mutex_lock(&mutex);
}
} }
void unlock() { void unlock() {

View File

@ -70,4 +70,15 @@ SET (star_extern_SOURCES
lua/lzio.c lua/lzio.c
) )
IF (STAR_USE_RPMALLOC)
SET(star_extern_HEADERS ${star_extern_HEADERS}
rpmalloc.h
rpnew.h
)
SET(star_extern_SOURCES ${star_extern_SOURCES}
rpmalloc.c
)
ENDIF()
ADD_LIBRARY (star_extern OBJECT ${star_extern_SOURCES} ${star_extern_HEADERS}) ADD_LIBRARY (star_extern OBJECT ${star_extern_SOURCES} ${star_extern_HEADERS})

File diff suppressed because it is too large Load Diff

View File

@ -18,7 +18,7 @@
# include <locale> # include <locale>
#endif #endif
#ifdef _WIN32 #if defined(_WIN32) && !defined(FMT_WINDOWS_NO_WCHAR)
# include <io.h> // _isatty # include <io.h> // _isatty
#endif #endif
@ -58,8 +58,8 @@ FMT_FUNC void format_error_code(detail::buffer<char>& out, int error_code,
error_code_size += detail::to_unsigned(detail::count_digits(abs_value)); error_code_size += detail::to_unsigned(detail::count_digits(abs_value));
auto it = buffer_appender<char>(out); auto it = buffer_appender<char>(out);
if (message.size() <= inline_buffer_size - error_code_size) if (message.size() <= inline_buffer_size - error_code_size)
format_to(it, FMT_STRING("{}{}"), message, SEP); fmt::format_to(it, FMT_STRING("{}{}"), message, SEP);
format_to(it, FMT_STRING("{}{}"), ERROR_STR, error_code); fmt::format_to(it, FMT_STRING("{}{}"), ERROR_STR, error_code);
FMT_ASSERT(out.size() <= inline_buffer_size, ""); FMT_ASSERT(out.size() <= inline_buffer_size, "");
} }
@ -73,9 +73,8 @@ FMT_FUNC void report_error(format_func func, int error_code,
} }
// A wrapper around fwrite that throws on error. // A wrapper around fwrite that throws on error.
inline void fwrite_fully(const void* ptr, size_t size, size_t count, inline void fwrite_fully(const void* ptr, size_t count, FILE* stream) {
FILE* stream) { size_t written = std::fwrite(ptr, 1, count, stream);
size_t written = std::fwrite(ptr, size, count, stream);
if (written < count) if (written < count)
FMT_THROW(system_error(errno, FMT_STRING("cannot write to file"))); FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
} }
@ -86,7 +85,7 @@ locale_ref::locale_ref(const Locale& loc) : locale_(&loc) {
static_assert(std::is_same<Locale, std::locale>::value, ""); static_assert(std::is_same<Locale, std::locale>::value, "");
} }
template <typename Locale> Locale locale_ref::get() const { template <typename Locale> auto locale_ref::get() const -> Locale {
static_assert(std::is_same<Locale, std::locale>::value, ""); static_assert(std::is_same<Locale, std::locale>::value, "");
return locale_ ? *static_cast<const std::locale*>(locale_) : std::locale(); return locale_ ? *static_cast<const std::locale*>(locale_) : std::locale();
} }
@ -98,7 +97,8 @@ FMT_FUNC auto thousands_sep_impl(locale_ref loc) -> thousands_sep_result<Char> {
auto thousands_sep = grouping.empty() ? Char() : facet.thousands_sep(); auto thousands_sep = grouping.empty() ? Char() : facet.thousands_sep();
return {std::move(grouping), thousands_sep}; return {std::move(grouping), thousands_sep};
} }
template <typename Char> FMT_FUNC Char decimal_point_impl(locale_ref loc) { template <typename Char>
FMT_FUNC auto decimal_point_impl(locale_ref loc) -> Char {
return std::use_facet<std::numpunct<Char>>(loc.get<std::locale>()) return std::use_facet<std::numpunct<Char>>(loc.get<std::locale>())
.decimal_point(); .decimal_point();
} }
@ -144,24 +144,25 @@ FMT_API FMT_FUNC auto format_facet<std::locale>::do_put(
} }
#endif #endif
FMT_FUNC std::system_error vsystem_error(int error_code, string_view fmt, FMT_FUNC auto vsystem_error(int error_code, string_view fmt, format_args args)
format_args args) { -> std::system_error {
auto ec = std::error_code(error_code, std::generic_category()); auto ec = std::error_code(error_code, std::generic_category());
return std::system_error(ec, vformat(fmt, args)); return std::system_error(ec, vformat(fmt, args));
} }
namespace detail { namespace detail {
template <typename F> inline bool operator==(basic_fp<F> x, basic_fp<F> y) { template <typename F>
inline auto operator==(basic_fp<F> x, basic_fp<F> y) -> bool {
return x.f == y.f && x.e == y.e; return x.f == y.f && x.e == y.e;
} }
// Compilers should be able to optimize this into the ror instruction. // Compilers should be able to optimize this into the ror instruction.
FMT_CONSTEXPR inline uint32_t rotr(uint32_t n, uint32_t r) noexcept { FMT_CONSTEXPR inline auto rotr(uint32_t n, uint32_t r) noexcept -> uint32_t {
r &= 31; r &= 31;
return (n >> r) | (n << (32 - r)); return (n >> r) | (n << (32 - r));
} }
FMT_CONSTEXPR inline uint64_t rotr(uint64_t n, uint32_t r) noexcept { FMT_CONSTEXPR inline auto rotr(uint64_t n, uint32_t r) noexcept -> uint64_t {
r &= 63; r &= 63;
return (n >> r) | (n << (64 - r)); return (n >> r) | (n << (64 - r));
} }
@ -170,14 +171,14 @@ FMT_CONSTEXPR inline uint64_t rotr(uint64_t n, uint32_t r) noexcept {
namespace dragonbox { namespace dragonbox {
// Computes upper 64 bits of multiplication of a 32-bit unsigned integer and a // Computes upper 64 bits of multiplication of a 32-bit unsigned integer and a
// 64-bit unsigned integer. // 64-bit unsigned integer.
inline uint64_t umul96_upper64(uint32_t x, uint64_t y) noexcept { inline auto umul96_upper64(uint32_t x, uint64_t y) noexcept -> uint64_t {
return umul128_upper64(static_cast<uint64_t>(x) << 32, y); return umul128_upper64(static_cast<uint64_t>(x) << 32, y);
} }
// Computes lower 128 bits of multiplication of a 64-bit unsigned integer and a // Computes lower 128 bits of multiplication of a 64-bit unsigned integer and a
// 128-bit unsigned integer. // 128-bit unsigned integer.
inline uint128_fallback umul192_lower128(uint64_t x, inline auto umul192_lower128(uint64_t x, uint128_fallback y) noexcept
uint128_fallback y) noexcept { -> uint128_fallback {
uint64_t high = x * y.high(); uint64_t high = x * y.high();
uint128_fallback high_low = umul128(x, y.low()); uint128_fallback high_low = umul128(x, y.low());
return {high + high_low.high(), high_low.low()}; return {high + high_low.high(), high_low.low()};
@ -185,12 +186,12 @@ inline uint128_fallback umul192_lower128(uint64_t x,
// Computes lower 64 bits of multiplication of a 32-bit unsigned integer and a // Computes lower 64 bits of multiplication of a 32-bit unsigned integer and a
// 64-bit unsigned integer. // 64-bit unsigned integer.
inline uint64_t umul96_lower64(uint32_t x, uint64_t y) noexcept { inline auto umul96_lower64(uint32_t x, uint64_t y) noexcept -> uint64_t {
return x * y; return x * y;
} }
// Various fast log computations. // Various fast log computations.
inline int floor_log10_pow2_minus_log10_4_over_3(int e) noexcept { inline auto floor_log10_pow2_minus_log10_4_over_3(int e) noexcept -> int {
FMT_ASSERT(e <= 2936 && e >= -2985, "too large exponent"); FMT_ASSERT(e <= 2936 && e >= -2985, "too large exponent");
return (e * 631305 - 261663) >> 21; return (e * 631305 - 261663) >> 21;
} }
@ -204,7 +205,7 @@ FMT_INLINE_VARIABLE constexpr struct {
// divisible by pow(10, N). // divisible by pow(10, N).
// Precondition: n <= pow(10, N + 1). // Precondition: n <= pow(10, N + 1).
template <int N> template <int N>
bool check_divisibility_and_divide_by_pow10(uint32_t& n) noexcept { auto check_divisibility_and_divide_by_pow10(uint32_t& n) noexcept -> bool {
// The numbers below are chosen such that: // The numbers below are chosen such that:
// 1. floor(n/d) = floor(nm / 2^k) where d=10 or d=100, // 1. floor(n/d) = floor(nm / 2^k) where d=10 or d=100,
// 2. nm mod 2^k < m if and only if n is divisible by d, // 2. nm mod 2^k < m if and only if n is divisible by d,
@ -229,7 +230,7 @@ bool check_divisibility_and_divide_by_pow10(uint32_t& n) noexcept {
// Computes floor(n / pow(10, N)) for small n and N. // Computes floor(n / pow(10, N)) for small n and N.
// Precondition: n <= pow(10, N + 1). // Precondition: n <= pow(10, N + 1).
template <int N> uint32_t small_division_by_pow10(uint32_t n) noexcept { template <int N> auto small_division_by_pow10(uint32_t n) noexcept -> uint32_t {
constexpr auto info = div_small_pow10_infos[N - 1]; constexpr auto info = div_small_pow10_infos[N - 1];
FMT_ASSERT(n <= info.divisor * 10, "n is too large"); FMT_ASSERT(n <= info.divisor * 10, "n is too large");
constexpr uint32_t magic_number = constexpr uint32_t magic_number =
@ -238,12 +239,12 @@ template <int N> uint32_t small_division_by_pow10(uint32_t n) noexcept {
} }
// Computes floor(n / 10^(kappa + 1)) (float) // Computes floor(n / 10^(kappa + 1)) (float)
inline uint32_t divide_by_10_to_kappa_plus_1(uint32_t n) noexcept { inline auto divide_by_10_to_kappa_plus_1(uint32_t n) noexcept -> uint32_t {
// 1374389535 = ceil(2^37/100) // 1374389535 = ceil(2^37/100)
return static_cast<uint32_t>((static_cast<uint64_t>(n) * 1374389535) >> 37); return static_cast<uint32_t>((static_cast<uint64_t>(n) * 1374389535) >> 37);
} }
// Computes floor(n / 10^(kappa + 1)) (double) // Computes floor(n / 10^(kappa + 1)) (double)
inline uint64_t divide_by_10_to_kappa_plus_1(uint64_t n) noexcept { inline auto divide_by_10_to_kappa_plus_1(uint64_t n) noexcept -> uint64_t {
// 2361183241434822607 = ceil(2^(64+7)/1000) // 2361183241434822607 = ceil(2^(64+7)/1000)
return umul128_upper64(n, 2361183241434822607ull) >> 7; return umul128_upper64(n, 2361183241434822607ull) >> 7;
} }
@ -255,7 +256,7 @@ template <> struct cache_accessor<float> {
using carrier_uint = float_info<float>::carrier_uint; using carrier_uint = float_info<float>::carrier_uint;
using cache_entry_type = uint64_t; using cache_entry_type = uint64_t;
static uint64_t get_cached_power(int k) noexcept { static auto get_cached_power(int k) noexcept -> uint64_t {
FMT_ASSERT(k >= float_info<float>::min_k && k <= float_info<float>::max_k, FMT_ASSERT(k >= float_info<float>::min_k && k <= float_info<float>::max_k,
"k is out of range"); "k is out of range");
static constexpr const uint64_t pow10_significands[] = { static constexpr const uint64_t pow10_significands[] = {
@ -297,20 +298,23 @@ template <> struct cache_accessor<float> {
bool is_integer; bool is_integer;
}; };
static compute_mul_result compute_mul( static auto compute_mul(carrier_uint u,
carrier_uint u, const cache_entry_type& cache) noexcept { const cache_entry_type& cache) noexcept
-> compute_mul_result {
auto r = umul96_upper64(u, cache); auto r = umul96_upper64(u, cache);
return {static_cast<carrier_uint>(r >> 32), return {static_cast<carrier_uint>(r >> 32),
static_cast<carrier_uint>(r) == 0}; static_cast<carrier_uint>(r) == 0};
} }
static uint32_t compute_delta(const cache_entry_type& cache, static auto compute_delta(const cache_entry_type& cache, int beta) noexcept
int beta) noexcept { -> uint32_t {
return static_cast<uint32_t>(cache >> (64 - 1 - beta)); return static_cast<uint32_t>(cache >> (64 - 1 - beta));
} }
static compute_mul_parity_result compute_mul_parity( static auto compute_mul_parity(carrier_uint two_f,
carrier_uint two_f, const cache_entry_type& cache, int beta) noexcept { const cache_entry_type& cache,
int beta) noexcept
-> compute_mul_parity_result {
FMT_ASSERT(beta >= 1, ""); FMT_ASSERT(beta >= 1, "");
FMT_ASSERT(beta < 64, ""); FMT_ASSERT(beta < 64, "");
@ -319,22 +323,22 @@ template <> struct cache_accessor<float> {
static_cast<uint32_t>(r >> (32 - beta)) == 0}; static_cast<uint32_t>(r >> (32 - beta)) == 0};
} }
static carrier_uint compute_left_endpoint_for_shorter_interval_case( static auto compute_left_endpoint_for_shorter_interval_case(
const cache_entry_type& cache, int beta) noexcept { const cache_entry_type& cache, int beta) noexcept -> carrier_uint {
return static_cast<carrier_uint>( return static_cast<carrier_uint>(
(cache - (cache >> (num_significand_bits<float>() + 2))) >> (cache - (cache >> (num_significand_bits<float>() + 2))) >>
(64 - num_significand_bits<float>() - 1 - beta)); (64 - num_significand_bits<float>() - 1 - beta));
} }
static carrier_uint compute_right_endpoint_for_shorter_interval_case( static auto compute_right_endpoint_for_shorter_interval_case(
const cache_entry_type& cache, int beta) noexcept { const cache_entry_type& cache, int beta) noexcept -> carrier_uint {
return static_cast<carrier_uint>( return static_cast<carrier_uint>(
(cache + (cache >> (num_significand_bits<float>() + 1))) >> (cache + (cache >> (num_significand_bits<float>() + 1))) >>
(64 - num_significand_bits<float>() - 1 - beta)); (64 - num_significand_bits<float>() - 1 - beta));
} }
static carrier_uint compute_round_up_for_shorter_interval_case( static auto compute_round_up_for_shorter_interval_case(
const cache_entry_type& cache, int beta) noexcept { const cache_entry_type& cache, int beta) noexcept -> carrier_uint {
return (static_cast<carrier_uint>( return (static_cast<carrier_uint>(
cache >> (64 - num_significand_bits<float>() - 2 - beta)) + cache >> (64 - num_significand_bits<float>() - 2 - beta)) +
1) / 1) /
@ -346,7 +350,7 @@ template <> struct cache_accessor<double> {
using carrier_uint = float_info<double>::carrier_uint; using carrier_uint = float_info<double>::carrier_uint;
using cache_entry_type = uint128_fallback; using cache_entry_type = uint128_fallback;
static uint128_fallback get_cached_power(int k) noexcept { static auto get_cached_power(int k) noexcept -> uint128_fallback {
FMT_ASSERT(k >= float_info<double>::min_k && k <= float_info<double>::max_k, FMT_ASSERT(k >= float_info<double>::min_k && k <= float_info<double>::max_k,
"k is out of range"); "k is out of range");
@ -985,8 +989,7 @@ template <> struct cache_accessor<double> {
{0xe0accfa875af45a7, 0x93eb1b80a33b8606}, {0xe0accfa875af45a7, 0x93eb1b80a33b8606},
{0x8c6c01c9498d8b88, 0xbc72f130660533c4}, {0x8c6c01c9498d8b88, 0xbc72f130660533c4},
{0xaf87023b9bf0ee6a, 0xeb8fad7c7f8680b5}, {0xaf87023b9bf0ee6a, 0xeb8fad7c7f8680b5},
{ 0xdb68c2ca82ed2a05, {0xdb68c2ca82ed2a05, 0xa67398db9f6820e2},
0xa67398db9f6820e2 }
#else #else
{0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7b}, {0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7b},
{0xce5d73ff402d98e3, 0xfb0a3d212dc81290}, {0xce5d73ff402d98e3, 0xfb0a3d212dc81290},
@ -1071,19 +1074,22 @@ template <> struct cache_accessor<double> {
bool is_integer; bool is_integer;
}; };
static compute_mul_result compute_mul( static auto compute_mul(carrier_uint u,
carrier_uint u, const cache_entry_type& cache) noexcept { const cache_entry_type& cache) noexcept
-> compute_mul_result {
auto r = umul192_upper128(u, cache); auto r = umul192_upper128(u, cache);
return {r.high(), r.low() == 0}; return {r.high(), r.low() == 0};
} }
static uint32_t compute_delta(cache_entry_type const& cache, static auto compute_delta(cache_entry_type const& cache, int beta) noexcept
int beta) noexcept { -> uint32_t {
return static_cast<uint32_t>(cache.high() >> (64 - 1 - beta)); return static_cast<uint32_t>(cache.high() >> (64 - 1 - beta));
} }
static compute_mul_parity_result compute_mul_parity( static auto compute_mul_parity(carrier_uint two_f,
carrier_uint two_f, const cache_entry_type& cache, int beta) noexcept { const cache_entry_type& cache,
int beta) noexcept
-> compute_mul_parity_result {
FMT_ASSERT(beta >= 1, ""); FMT_ASSERT(beta >= 1, "");
FMT_ASSERT(beta < 64, ""); FMT_ASSERT(beta < 64, "");
@ -1092,35 +1098,35 @@ template <> struct cache_accessor<double> {
((r.high() << beta) | (r.low() >> (64 - beta))) == 0}; ((r.high() << beta) | (r.low() >> (64 - beta))) == 0};
} }
static carrier_uint compute_left_endpoint_for_shorter_interval_case( static auto compute_left_endpoint_for_shorter_interval_case(
const cache_entry_type& cache, int beta) noexcept { const cache_entry_type& cache, int beta) noexcept -> carrier_uint {
return (cache.high() - return (cache.high() -
(cache.high() >> (num_significand_bits<double>() + 2))) >> (cache.high() >> (num_significand_bits<double>() + 2))) >>
(64 - num_significand_bits<double>() - 1 - beta); (64 - num_significand_bits<double>() - 1 - beta);
} }
static carrier_uint compute_right_endpoint_for_shorter_interval_case( static auto compute_right_endpoint_for_shorter_interval_case(
const cache_entry_type& cache, int beta) noexcept { const cache_entry_type& cache, int beta) noexcept -> carrier_uint {
return (cache.high() + return (cache.high() +
(cache.high() >> (num_significand_bits<double>() + 1))) >> (cache.high() >> (num_significand_bits<double>() + 1))) >>
(64 - num_significand_bits<double>() - 1 - beta); (64 - num_significand_bits<double>() - 1 - beta);
} }
static carrier_uint compute_round_up_for_shorter_interval_case( static auto compute_round_up_for_shorter_interval_case(
const cache_entry_type& cache, int beta) noexcept { const cache_entry_type& cache, int beta) noexcept -> carrier_uint {
return ((cache.high() >> (64 - num_significand_bits<double>() - 2 - beta)) + return ((cache.high() >> (64 - num_significand_bits<double>() - 2 - beta)) +
1) / 1) /
2; 2;
} }
}; };
FMT_FUNC uint128_fallback get_cached_power(int k) noexcept { FMT_FUNC auto get_cached_power(int k) noexcept -> uint128_fallback {
return cache_accessor<double>::get_cached_power(k); return cache_accessor<double>::get_cached_power(k);
} }
// Various integer checks // Various integer checks
template <typename T> template <typename T>
bool is_left_endpoint_integer_shorter_interval(int exponent) noexcept { auto is_left_endpoint_integer_shorter_interval(int exponent) noexcept -> bool {
const int case_shorter_interval_left_endpoint_lower_threshold = 2; const int case_shorter_interval_left_endpoint_lower_threshold = 2;
const int case_shorter_interval_left_endpoint_upper_threshold = 3; const int case_shorter_interval_left_endpoint_upper_threshold = 3;
return exponent >= case_shorter_interval_left_endpoint_lower_threshold && return exponent >= case_shorter_interval_left_endpoint_lower_threshold &&
@ -1128,16 +1134,12 @@ bool is_left_endpoint_integer_shorter_interval(int exponent) noexcept {
} }
// Remove trailing zeros from n and return the number of zeros removed (float) // Remove trailing zeros from n and return the number of zeros removed (float)
FMT_INLINE int remove_trailing_zeros(uint32_t& n) noexcept { FMT_INLINE int remove_trailing_zeros(uint32_t& n, int s = 0) noexcept {
FMT_ASSERT(n != 0, ""); FMT_ASSERT(n != 0, "");
// Modular inverse of 5 (mod 2^32): (mod_inv_5 * 5) mod 2^32 = 1. // Modular inverse of 5 (mod 2^32): (mod_inv_5 * 5) mod 2^32 = 1.
// See https://github.com/fmtlib/fmt/issues/3163 for more details. constexpr uint32_t mod_inv_5 = 0xcccccccd;
const uint32_t mod_inv_5 = 0xcccccccd; constexpr uint32_t mod_inv_25 = 0xc28f5c29; // = mod_inv_5 * mod_inv_5
// Casts are needed to workaround a bug in MSVC 19.22 and older.
const uint32_t mod_inv_25 =
static_cast<uint32_t>(uint64_t(mod_inv_5) * mod_inv_5);
int s = 0;
while (true) { while (true) {
auto q = rotr(n * mod_inv_25, 2); auto q = rotr(n * mod_inv_25, 2);
if (q > max_value<uint32_t>() / 100) break; if (q > max_value<uint32_t>() / 100) break;
@ -1162,32 +1164,17 @@ FMT_INLINE int remove_trailing_zeros(uint64_t& n) noexcept {
// Is n is divisible by 10^8? // Is n is divisible by 10^8?
if ((nm.high() & ((1ull << (90 - 64)) - 1)) == 0 && nm.low() < magic_number) { if ((nm.high() & ((1ull << (90 - 64)) - 1)) == 0 && nm.low() < magic_number) {
// If yes, work with the quotient. // If yes, work with the quotient...
auto n32 = static_cast<uint32_t>(nm.high() >> (90 - 64)); auto n32 = static_cast<uint32_t>(nm.high() >> (90 - 64));
// ... and use the 32 bit variant of the function
const uint32_t mod_inv_5 = 0xcccccccd; int s = remove_trailing_zeros(n32, 8);
const uint32_t mod_inv_25 = mod_inv_5 * mod_inv_5;
int s = 8;
while (true) {
auto q = rotr(n32 * mod_inv_25, 2);
if (q > max_value<uint32_t>() / 100) break;
n32 = q;
s += 2;
}
auto q = rotr(n32 * mod_inv_5, 1);
if (q <= max_value<uint32_t>() / 10) {
n32 = q;
s |= 1;
}
n = n32; n = n32;
return s; return s;
} }
// If n is not divisible by 10^8, work with n itself. // If n is not divisible by 10^8, work with n itself.
const uint64_t mod_inv_5 = 0xcccccccccccccccd; constexpr uint64_t mod_inv_5 = 0xcccccccccccccccd;
const uint64_t mod_inv_25 = mod_inv_5 * mod_inv_5; constexpr uint64_t mod_inv_25 = 0x8f5c28f5c28f5c29; // mod_inv_5 * mod_inv_5
int s = 0; int s = 0;
while (true) { while (true) {
@ -1253,7 +1240,7 @@ FMT_INLINE decimal_fp<T> shorter_interval_case(int exponent) noexcept {
return ret_value; return ret_value;
} }
template <typename T> decimal_fp<T> to_decimal(T x) noexcept { template <typename T> auto to_decimal(T x) noexcept -> decimal_fp<T> {
// Step 1: integer promotion & Schubfach multiplier calculation. // Step 1: integer promotion & Schubfach multiplier calculation.
using carrier_uint = typename float_info<T>::carrier_uint; using carrier_uint = typename float_info<T>::carrier_uint;
@ -1392,15 +1379,15 @@ template <> struct formatter<detail::bigint> {
for (auto i = n.bigits_.size(); i > 0; --i) { for (auto i = n.bigits_.size(); i > 0; --i) {
auto value = n.bigits_[i - 1u]; auto value = n.bigits_[i - 1u];
if (first) { if (first) {
out = format_to(out, FMT_STRING("{:x}"), value); out = fmt::format_to(out, FMT_STRING("{:x}"), value);
first = false; first = false;
continue; continue;
} }
out = format_to(out, FMT_STRING("{:08x}"), value); out = fmt::format_to(out, FMT_STRING("{:08x}"), value);
} }
if (n.exp_ > 0) if (n.exp_ > 0)
out = format_to(out, FMT_STRING("p{}"), out = fmt::format_to(out, FMT_STRING("p{}"),
n.exp_ * detail::bigint::bigit_bits); n.exp_ * detail::bigint::bigit_bits);
return out; return out;
} }
}; };
@ -1436,7 +1423,7 @@ FMT_FUNC void report_system_error(int error_code,
report_error(format_system_error, error_code, message); report_error(format_system_error, error_code, message);
} }
FMT_FUNC std::string vformat(string_view fmt, format_args args) { FMT_FUNC auto vformat(string_view fmt, format_args args) -> std::string {
// Don't optimize the "{}" case to keep the binary size small and because it // Don't optimize the "{}" case to keep the binary size small and because it
// can be better optimized in fmt::format anyway. // can be better optimized in fmt::format anyway.
auto buffer = memory_buffer(); auto buffer = memory_buffer();
@ -1445,33 +1432,43 @@ FMT_FUNC std::string vformat(string_view fmt, format_args args) {
} }
namespace detail { namespace detail {
#ifndef _WIN32 #if !defined(_WIN32) || defined(FMT_WINDOWS_NO_WCHAR)
FMT_FUNC bool write_console(std::FILE*, string_view) { return false; } FMT_FUNC auto write_console(int, string_view) -> bool { return false; }
FMT_FUNC auto write_console(std::FILE*, string_view) -> bool { return false; }
#else #else
using dword = conditional_t<sizeof(long) == 4, unsigned long, unsigned>; using dword = conditional_t<sizeof(long) == 4, unsigned long, unsigned>;
extern "C" __declspec(dllimport) int __stdcall WriteConsoleW( // extern "C" __declspec(dllimport) int __stdcall WriteConsoleW( //
void*, const void*, dword, dword*, void*); void*, const void*, dword, dword*, void*);
FMT_FUNC bool write_console(std::FILE* f, string_view text) { FMT_FUNC bool write_console(int fd, string_view text) {
auto fd = _fileno(f);
if (!_isatty(fd)) return false;
auto u16 = utf8_to_utf16(text); auto u16 = utf8_to_utf16(text);
auto written = dword();
return WriteConsoleW(reinterpret_cast<void*>(_get_osfhandle(fd)), u16.c_str(), return WriteConsoleW(reinterpret_cast<void*>(_get_osfhandle(fd)), u16.c_str(),
static_cast<uint32_t>(u16.size()), &written, nullptr); static_cast<dword>(u16.size()), nullptr, nullptr) != 0;
} }
FMT_FUNC auto write_console(std::FILE* f, string_view text) -> bool {
return write_console(_fileno(f), text);
}
#endif
#ifdef _WIN32
// Print assuming legacy (non-Unicode) encoding. // Print assuming legacy (non-Unicode) encoding.
FMT_FUNC void vprint_mojibake(std::FILE* f, string_view fmt, format_args args) { FMT_FUNC void vprint_mojibake(std::FILE* f, string_view fmt, format_args args) {
auto buffer = memory_buffer(); auto buffer = memory_buffer();
detail::vformat_to(buffer, fmt, detail::vformat_to(buffer, fmt, args);
basic_format_args<buffer_context<char>>(args)); fwrite_fully(buffer.data(), buffer.size(), f);
fwrite_fully(buffer.data(), 1, buffer.size(), f);
} }
#endif #endif
FMT_FUNC void print(std::FILE* f, string_view text) { FMT_FUNC void print(std::FILE* f, string_view text) {
if (!write_console(f, text)) fwrite_fully(text.data(), 1, text.size(), f); #ifdef _WIN32
int fd = _fileno(f);
if (_isatty(fd)) {
std::fflush(f);
if (write_console(fd, text)) return;
}
#endif
fwrite_fully(text.data(), text.size(), f);
} }
} // namespace detail } // namespace detail

File diff suppressed because it is too large Load Diff

View File

@ -10,19 +10,50 @@
#include <fstream> // std::filebuf #include <fstream> // std::filebuf
#if defined(_WIN32) && defined(__GLIBCXX__) #ifdef _WIN32
# include <ext/stdio_filebuf.h> # ifdef __GLIBCXX__
# include <ext/stdio_sync_filebuf.h> # include <ext/stdio_filebuf.h>
#elif defined(_WIN32) && defined(_LIBCPP_VERSION) # include <ext/stdio_sync_filebuf.h>
# include <__std_stream> # endif
# include <io.h>
#endif #endif
#include "format.h" #include "format.h"
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
namespace detail { namespace detail {
template <typename Streambuf> class formatbuf : public Streambuf {
private:
using char_type = typename Streambuf::char_type;
using streamsize = decltype(std::declval<Streambuf>().sputn(nullptr, 0));
using int_type = typename Streambuf::int_type;
using traits_type = typename Streambuf::traits_type;
buffer<char_type>& buffer_;
public:
explicit formatbuf(buffer<char_type>& buf) : buffer_(buf) {}
protected:
// The put area is always empty. This makes the implementation simpler and has
// the advantage that the streambuf and the buffer are always in sync and
// sputc never writes into uninitialized memory. A disadvantage is that each
// call to sputc always results in a (virtual) call to overflow. There is no
// disadvantage here for sputn since this always results in a call to xsputn.
auto overflow(int_type ch) -> int_type override {
if (!traits_type::eq_int_type(ch, traits_type::eof()))
buffer_.push_back(static_cast<char_type>(ch));
return ch;
}
auto xsputn(const char_type* s, streamsize count) -> streamsize override {
buffer_.append(s, s + count);
return count;
}
};
// Generate a unique explicit instantion in every translation unit using a tag // Generate a unique explicit instantion in every translation unit using a tag
// type in an anonymous namespace. // type in an anonymous namespace.
namespace { namespace {
@ -37,36 +68,40 @@ class file_access {
template class file_access<file_access_tag, std::filebuf, template class file_access<file_access_tag, std::filebuf,
&std::filebuf::_Myfile>; &std::filebuf::_Myfile>;
auto get_file(std::filebuf&) -> FILE*; auto get_file(std::filebuf&) -> FILE*;
#elif defined(_WIN32) && defined(_LIBCPP_VERSION)
template class file_access<file_access_tag, std::__stdoutbuf<char>,
&std::__stdoutbuf<char>::__file_>;
auto get_file(std::__stdoutbuf<char>&) -> FILE*;
#endif #endif
inline bool write_ostream_unicode(std::ostream& os, fmt::string_view data) { inline auto write_ostream_unicode(std::ostream& os, fmt::string_view data)
-> bool {
FILE* f = nullptr;
#if FMT_MSC_VERSION #if FMT_MSC_VERSION
if (auto* buf = dynamic_cast<std::filebuf*>(os.rdbuf())) if (auto* buf = dynamic_cast<std::filebuf*>(os.rdbuf()))
if (FILE* f = get_file(*buf)) return write_console(f, data); f = get_file(*buf);
#elif defined(_WIN32) && defined(__GLIBCXX__) else
auto* rdbuf = os.rdbuf(); return false;
FILE* c_file; #elif defined(_WIN32) && defined(__GLIBCXX__)
if (auto* sfbuf = dynamic_cast<__gnu_cxx::stdio_sync_filebuf<char>*>(rdbuf)) auto* rdbuf = os.rdbuf();
c_file = sfbuf->file(); if (auto* sfbuf = dynamic_cast<__gnu_cxx::stdio_sync_filebuf<char>*>(rdbuf))
else if (auto* fbuf = dynamic_cast<__gnu_cxx::stdio_filebuf<char>*>(rdbuf)) f = sfbuf->file();
c_file = fbuf->file(); else if (auto* fbuf = dynamic_cast<__gnu_cxx::stdio_filebuf<char>*>(rdbuf))
f = fbuf->file();
else else
return false; return false;
if (c_file) return write_console(c_file, data);
#elif defined(_WIN32) && defined(_LIBCPP_VERSION)
if (auto* buf = dynamic_cast<std::__stdoutbuf<char>*>(os.rdbuf()))
if (FILE* f = get_file(*buf)) return write_console(f, data);
#else #else
ignore_unused(os, data); ignore_unused(os, data, f);
#endif
#ifdef _WIN32
if (f) {
int fd = _fileno(f);
if (_isatty(fd)) {
os.flush();
return write_console(fd, data);
}
}
#endif #endif
return false; return false;
} }
inline bool write_ostream_unicode(std::wostream&, inline auto write_ostream_unicode(std::wostream&,
fmt::basic_string_view<wchar_t>) { fmt::basic_string_view<wchar_t>) -> bool {
return false; return false;
} }
@ -87,18 +122,19 @@ void write_buffer(std::basic_ostream<Char>& os, buffer<Char>& buf) {
} }
template <typename Char, typename T> template <typename Char, typename T>
void format_value(buffer<Char>& buf, const T& value, void format_value(buffer<Char>& buf, const T& value) {
locale_ref loc = locale_ref()) {
auto&& format_buf = formatbuf<std::basic_streambuf<Char>>(buf); auto&& format_buf = formatbuf<std::basic_streambuf<Char>>(buf);
auto&& output = std::basic_ostream<Char>(&format_buf); auto&& output = std::basic_ostream<Char>(&format_buf);
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR) #if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
if (loc) output.imbue(loc.get<std::locale>()); output.imbue(std::locale::classic()); // The default is always unlocalized.
#endif #endif
output << value; output << value;
output.exceptions(std::ios_base::failbit | std::ios_base::badbit); output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
} }
template <typename T> struct streamed_view { const T& value; }; template <typename T> struct streamed_view {
const T& value;
};
} // namespace detail } // namespace detail
@ -111,7 +147,7 @@ struct basic_ostream_formatter : formatter<basic_string_view<Char>, Char> {
auto format(const T& value, basic_format_context<OutputIt, Char>& ctx) const auto format(const T& value, basic_format_context<OutputIt, Char>& ctx) const
-> OutputIt { -> OutputIt {
auto buffer = basic_memory_buffer<Char>(); auto buffer = basic_memory_buffer<Char>();
detail::format_value(buffer, value, ctx.locale()); detail::format_value(buffer, value);
return formatter<basic_string_view<Char>, Char>::format( return formatter<basic_string_view<Char>, Char>::format(
{buffer.data(), buffer.size()}, ctx); {buffer.data(), buffer.size()}, ctx);
} }
@ -140,7 +176,7 @@ struct formatter<detail::streamed_view<T>, Char>
\endrst \endrst
*/ */
template <typename T> template <typename T>
auto streamed(const T& value) -> detail::streamed_view<T> { constexpr auto streamed(const T& value) -> detail::streamed_view<T> {
return {value}; return {value};
} }
@ -155,7 +191,7 @@ inline void vprint_directly(std::ostream& os, string_view format_str,
} // namespace detail } // namespace detail
FMT_MODULE_EXPORT template <typename Char> FMT_EXPORT template <typename Char>
void vprint(std::basic_ostream<Char>& os, void vprint(std::basic_ostream<Char>& os,
basic_string_view<type_identity_t<Char>> format_str, basic_string_view<type_identity_t<Char>> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) { basic_format_args<buffer_context<type_identity_t<Char>>> args) {
@ -174,7 +210,7 @@ void vprint(std::basic_ostream<Char>& os,
fmt::print(cerr, "Don't {}!", "panic"); fmt::print(cerr, "Don't {}!", "panic");
\endrst \endrst
*/ */
FMT_MODULE_EXPORT template <typename... T> FMT_EXPORT template <typename... T>
void print(std::ostream& os, format_string<T...> fmt, T&&... args) { void print(std::ostream& os, format_string<T...> fmt, T&&... args) {
const auto& vargs = fmt::make_format_args(args...); const auto& vargs = fmt::make_format_args(args...);
if (detail::is_utf8()) if (detail::is_utf8())
@ -183,7 +219,7 @@ void print(std::ostream& os, format_string<T...> fmt, T&&... args) {
detail::vprint_directly(os, fmt, vargs); detail::vprint_directly(os, fmt, vargs);
} }
FMT_MODULE_EXPORT FMT_EXPORT
template <typename... Args> template <typename... Args>
void print(std::wostream& os, void print(std::wostream& os,
basic_format_string<wchar_t, type_identity_t<Args>...> fmt, basic_format_string<wchar_t, type_identity_t<Args>...> fmt,
@ -191,12 +227,12 @@ void print(std::wostream& os,
vprint(os, fmt, fmt::make_format_args<buffer_context<wchar_t>>(args...)); vprint(os, fmt, fmt::make_format_args<buffer_context<wchar_t>>(args...));
} }
FMT_MODULE_EXPORT template <typename... T> FMT_EXPORT template <typename... T>
void println(std::ostream& os, format_string<T...> fmt, T&&... args) { void println(std::ostream& os, format_string<T...> fmt, T&&... args) {
fmt::print(os, "{}\n", fmt::format(fmt, std::forward<T>(args)...)); fmt::print(os, "{}\n", fmt::format(fmt, std::forward<T>(args)...));
} }
FMT_MODULE_EXPORT FMT_EXPORT
template <typename... Args> template <typename... Args>
void println(std::wostream& os, void println(std::wostream& os,
basic_format_string<wchar_t, type_identity_t<Args>...> fmt, basic_format_string<wchar_t, type_identity_t<Args>...> fmt,

View File

@ -16,22 +16,22 @@
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
FMT_BEGIN_EXPORT FMT_BEGIN_EXPORT
template <typename T> struct printf_formatter { printf_formatter() = delete; }; template <typename T> struct printf_formatter {
printf_formatter() = delete;
template <typename Char>
class basic_printf_parse_context : public basic_format_parse_context<Char> {
using basic_format_parse_context<Char>::basic_format_parse_context;
}; };
template <typename OutputIt, typename Char> class basic_printf_context { template <typename Char> class basic_printf_context {
private: private:
OutputIt out_; detail::buffer_appender<Char> out_;
basic_format_args<basic_printf_context> args_; basic_format_args<basic_printf_context> args_;
static_assert(std::is_same<Char, char>::value ||
std::is_same<Char, wchar_t>::value,
"Unsupported code unit type.");
public: public:
using char_type = Char; using char_type = Char;
using format_arg = basic_format_arg<basic_printf_context>; using parse_context_type = basic_format_parse_context<Char>;
using parse_context_type = basic_printf_parse_context<Char>;
template <typename T> using formatter_type = printf_formatter<T>; template <typename T> using formatter_type = printf_formatter<T>;
/** /**
@ -40,75 +40,77 @@ template <typename OutputIt, typename Char> class basic_printf_context {
stored in the context object so make sure they have appropriate lifetimes. stored in the context object so make sure they have appropriate lifetimes.
\endrst \endrst
*/ */
basic_printf_context(OutputIt out, basic_printf_context(detail::buffer_appender<Char> out,
basic_format_args<basic_printf_context> args) basic_format_args<basic_printf_context> args)
: out_(out), args_(args) {} : out_(out), args_(args) {}
OutputIt out() { return out_; } auto out() -> detail::buffer_appender<Char> { return out_; }
void advance_to(OutputIt it) { out_ = it; } void advance_to(detail::buffer_appender<Char>) {}
detail::locale_ref locale() { return {}; } auto locale() -> detail::locale_ref { return {}; }
format_arg arg(int id) const { return args_.get(id); } auto arg(int id) const -> basic_format_arg<basic_printf_context> {
return args_.get(id);
}
FMT_CONSTEXPR void on_error(const char* message) { FMT_CONSTEXPR void on_error(const char* message) {
detail::error_handler().on_error(message); detail::error_handler().on_error(message);
} }
}; };
FMT_BEGIN_DETAIL_NAMESPACE namespace detail {
// Checks if a value fits in int - used to avoid warnings about comparing // Checks if a value fits in int - used to avoid warnings about comparing
// signed and unsigned integers. // signed and unsigned integers.
template <bool IsSigned> struct int_checker { template <bool IsSigned> struct int_checker {
template <typename T> static bool fits_in_int(T value) { template <typename T> static auto fits_in_int(T value) -> bool {
unsigned max = max_value<int>(); unsigned max = max_value<int>();
return value <= max; return value <= max;
} }
static bool fits_in_int(bool) { return true; } static auto fits_in_int(bool) -> bool { return true; }
}; };
template <> struct int_checker<true> { template <> struct int_checker<true> {
template <typename T> static bool fits_in_int(T value) { template <typename T> static auto fits_in_int(T value) -> bool {
return value >= (std::numeric_limits<int>::min)() && return value >= (std::numeric_limits<int>::min)() &&
value <= max_value<int>(); value <= max_value<int>();
} }
static bool fits_in_int(int) { return true; } static auto fits_in_int(int) -> bool { return true; }
}; };
class printf_precision_handler { struct printf_precision_handler {
public:
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)> template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
int operator()(T value) { auto operator()(T value) -> int {
if (!int_checker<std::numeric_limits<T>::is_signed>::fits_in_int(value)) if (!int_checker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
throw_format_error("number is too big"); throw_format_error("number is too big");
return (std::max)(static_cast<int>(value), 0); return (std::max)(static_cast<int>(value), 0);
} }
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)> template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
int operator()(T) { auto operator()(T) -> int {
throw_format_error("precision is not integer"); throw_format_error("precision is not integer");
return 0; return 0;
} }
}; };
// An argument visitor that returns true iff arg is a zero integer. // An argument visitor that returns true iff arg is a zero integer.
class is_zero_int { struct is_zero_int {
public:
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)> template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
bool operator()(T value) { auto operator()(T value) -> bool {
return value == 0; return value == 0;
} }
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)> template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
bool operator()(T) { auto operator()(T) -> bool {
return false; return false;
} }
}; };
template <typename T> struct make_unsigned_or_bool : std::make_unsigned<T> {}; template <typename T> struct make_unsigned_or_bool : std::make_unsigned<T> {};
template <> struct make_unsigned_or_bool<bool> { using type = bool; }; template <> struct make_unsigned_or_bool<bool> {
using type = bool;
};
template <typename T, typename Context> class arg_converter { template <typename T, typename Context> class arg_converter {
private: private:
@ -132,22 +134,23 @@ template <typename T, typename Context> class arg_converter {
if (const_check(sizeof(target_type) <= sizeof(int))) { if (const_check(sizeof(target_type) <= sizeof(int))) {
// Extra casts are used to silence warnings. // Extra casts are used to silence warnings.
if (is_signed) { if (is_signed) {
arg_ = detail::make_arg<Context>( auto n = static_cast<int>(static_cast<target_type>(value));
static_cast<int>(static_cast<target_type>(value))); arg_ = detail::make_arg<Context>(n);
} else { } else {
using unsigned_type = typename make_unsigned_or_bool<target_type>::type; using unsigned_type = typename make_unsigned_or_bool<target_type>::type;
arg_ = detail::make_arg<Context>( auto n = static_cast<unsigned>(static_cast<unsigned_type>(value));
static_cast<unsigned>(static_cast<unsigned_type>(value))); arg_ = detail::make_arg<Context>(n);
} }
} else { } else {
if (is_signed) { if (is_signed) {
// glibc's printf doesn't sign extend arguments of smaller types: // glibc's printf doesn't sign extend arguments of smaller types:
// std::printf("%lld", -42); // prints "4294967254" // std::printf("%lld", -42); // prints "4294967254"
// but we don't have to do the same because it's a UB. // but we don't have to do the same because it's a UB.
arg_ = detail::make_arg<Context>(static_cast<long long>(value)); auto n = static_cast<long long>(value);
arg_ = detail::make_arg<Context>(n);
} else { } else {
arg_ = detail::make_arg<Context>( auto n = static_cast<typename make_unsigned_or_bool<U>::type>(value);
static_cast<typename make_unsigned_or_bool<U>::type>(value)); arg_ = detail::make_arg<Context>(n);
} }
} }
} }
@ -175,8 +178,8 @@ template <typename Context> class char_converter {
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)> template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
void operator()(T value) { void operator()(T value) {
arg_ = detail::make_arg<Context>( auto c = static_cast<typename Context::char_type>(value);
static_cast<typename Context::char_type>(value)); arg_ = detail::make_arg<Context>(c);
} }
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)> template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
@ -186,8 +189,8 @@ template <typename Context> class char_converter {
// An argument visitor that return a pointer to a C string if argument is a // An argument visitor that return a pointer to a C string if argument is a
// string or null otherwise. // string or null otherwise.
template <typename Char> struct get_cstring { template <typename Char> struct get_cstring {
template <typename T> const Char* operator()(T) { return nullptr; } template <typename T> auto operator()(T) -> const Char* { return nullptr; }
const Char* operator()(const Char* s) { return s; } auto operator()(const Char* s) -> const Char* { return s; }
}; };
// Checks if an argument is a valid printf width specifier and sets // Checks if an argument is a valid printf width specifier and sets
@ -200,7 +203,7 @@ template <typename Char> class printf_width_handler {
explicit printf_width_handler(format_specs<Char>& specs) : specs_(specs) {} explicit printf_width_handler(format_specs<Char>& specs) : specs_(specs) {}
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)> template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
unsigned operator()(T value) { auto operator()(T value) -> unsigned {
auto width = static_cast<uint32_or_64_or_128_t<T>>(value); auto width = static_cast<uint32_or_64_or_128_t<T>>(value);
if (detail::is_negative(value)) { if (detail::is_negative(value)) {
specs_.align = align::left; specs_.align = align::left;
@ -212,7 +215,7 @@ template <typename Char> class printf_width_handler {
} }
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)> template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
unsigned operator()(T) { auto operator()(T) -> unsigned {
throw_format_error("width is not integer"); throw_format_error("width is not integer");
return 0; return 0;
} }
@ -227,80 +230,85 @@ auto make_arg_formatter(buffer_appender<Char> iter, format_specs<Char>& s)
} }
// The ``printf`` argument formatter. // The ``printf`` argument formatter.
template <typename OutputIt, typename Char> template <typename Char>
class printf_arg_formatter : public arg_formatter<Char> { class printf_arg_formatter : public arg_formatter<Char> {
private: private:
using base = arg_formatter<Char>; using base = arg_formatter<Char>;
using context_type = basic_printf_context<OutputIt, Char>; using context_type = basic_printf_context<Char>;
context_type& context_; context_type& context_;
OutputIt write_null_pointer(bool is_string = false) { void write_null_pointer(bool is_string = false) {
auto s = this->specs; auto s = this->specs;
s.type = presentation_type::none; s.type = presentation_type::none;
return write_bytes(this->out, is_string ? "(null)" : "(nil)", s); write_bytes(this->out, is_string ? "(null)" : "(nil)", s);
} }
public: public:
printf_arg_formatter(OutputIt iter, format_specs<Char>& s, context_type& ctx) printf_arg_formatter(buffer_appender<Char> iter, format_specs<Char>& s,
context_type& ctx)
: base(make_arg_formatter(iter, s)), context_(ctx) {} : base(make_arg_formatter(iter, s)), context_(ctx) {}
OutputIt operator()(monostate value) { return base::operator()(value); } void operator()(monostate value) { base::operator()(value); }
template <typename T, FMT_ENABLE_IF(detail::is_integral<T>::value)> template <typename T, FMT_ENABLE_IF(detail::is_integral<T>::value)>
OutputIt operator()(T value) { void operator()(T value) {
// MSVC2013 fails to compile separate overloads for bool and Char so use // MSVC2013 fails to compile separate overloads for bool and Char so use
// std::is_same instead. // std::is_same instead.
if (std::is_same<T, Char>::value) { if (!std::is_same<T, Char>::value) {
format_specs<Char> fmt_specs = this->specs; base::operator()(value);
if (fmt_specs.type != presentation_type::none && return;
fmt_specs.type != presentation_type::chr) {
return (*this)(static_cast<int>(value));
}
fmt_specs.sign = sign::none;
fmt_specs.alt = false;
fmt_specs.fill[0] = ' '; // Ignore '0' flag for char types.
// align::numeric needs to be overwritten here since the '0' flag is
// ignored for non-numeric types
if (fmt_specs.align == align::none || fmt_specs.align == align::numeric)
fmt_specs.align = align::right;
return write<Char>(this->out, static_cast<Char>(value), fmt_specs);
} }
return base::operator()(value); format_specs<Char> fmt_specs = this->specs;
if (fmt_specs.type != presentation_type::none &&
fmt_specs.type != presentation_type::chr) {
return (*this)(static_cast<int>(value));
}
fmt_specs.sign = sign::none;
fmt_specs.alt = false;
fmt_specs.fill[0] = ' '; // Ignore '0' flag for char types.
// align::numeric needs to be overwritten here since the '0' flag is
// ignored for non-numeric types
if (fmt_specs.align == align::none || fmt_specs.align == align::numeric)
fmt_specs.align = align::right;
write<Char>(this->out, static_cast<Char>(value), fmt_specs);
} }
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)> template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
OutputIt operator()(T value) { void operator()(T value) {
return base::operator()(value); base::operator()(value);
} }
/** Formats a null-terminated C string. */ /** Formats a null-terminated C string. */
OutputIt operator()(const char* value) { void operator()(const char* value) {
if (value) return base::operator()(value); if (value)
return write_null_pointer(this->specs.type != presentation_type::pointer); base::operator()(value);
else
write_null_pointer(this->specs.type != presentation_type::pointer);
} }
/** Formats a null-terminated wide C string. */ /** Formats a null-terminated wide C string. */
OutputIt operator()(const wchar_t* value) { void operator()(const wchar_t* value) {
if (value) return base::operator()(value); if (value)
return write_null_pointer(this->specs.type != presentation_type::pointer); base::operator()(value);
else
write_null_pointer(this->specs.type != presentation_type::pointer);
} }
OutputIt operator()(basic_string_view<Char> value) { void operator()(basic_string_view<Char> value) { base::operator()(value); }
return base::operator()(value);
}
/** Formats a pointer. */ /** Formats a pointer. */
OutputIt operator()(const void* value) { void operator()(const void* value) {
return value ? base::operator()(value) : write_null_pointer(); if (value)
base::operator()(value);
else
write_null_pointer();
} }
/** Formats an argument of a custom (user-defined) type. */ /** Formats an argument of a custom (user-defined) type. */
OutputIt operator()(typename basic_format_arg<context_type>::handle handle) { void operator()(typename basic_format_arg<context_type>::handle handle) {
auto parse_ctx = auto parse_ctx = basic_format_parse_context<Char>({});
basic_printf_parse_context<Char>(basic_string_view<Char>());
handle.format(parse_ctx, context_); handle.format(parse_ctx, context_);
return this->out;
} }
}; };
@ -318,9 +326,7 @@ void parse_flags(format_specs<Char>& specs, const Char*& it, const Char* end) {
specs.fill[0] = '0'; specs.fill[0] = '0';
break; break;
case ' ': case ' ':
if (specs.sign != sign::plus) { if (specs.sign != sign::plus) specs.sign = sign::space;
specs.sign = sign::space;
}
break; break;
case '#': case '#':
specs.alt = true; specs.alt = true;
@ -332,8 +338,8 @@ void parse_flags(format_specs<Char>& specs, const Char*& it, const Char* end) {
} }
template <typename Char, typename GetArg> template <typename Char, typename GetArg>
int parse_header(const Char*& it, const Char* end, format_specs<Char>& specs, auto parse_header(const Char*& it, const Char* end, format_specs<Char>& specs,
GetArg get_arg) { GetArg get_arg) -> int {
int arg_index = -1; int arg_index = -1;
Char c = *it; Char c = *it;
if (c >= '0' && c <= '9') { if (c >= '0' && c <= '9') {
@ -414,8 +420,8 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
basic_format_args<Context> args) { basic_format_args<Context> args) {
using iterator = buffer_appender<Char>; using iterator = buffer_appender<Char>;
auto out = iterator(buf); auto out = iterator(buf);
auto context = basic_printf_context<iterator, Char>(out, args); auto context = basic_printf_context<Char>(out, args);
auto parse_ctx = basic_printf_parse_context<Char>(format); auto parse_ctx = basic_format_parse_context<Char>(format);
// Returns the argument with specified index or, if arg_index is -1, the next // Returns the argument with specified index or, if arg_index is -1, the next
// argument. // argument.
@ -437,12 +443,11 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
} }
Char c = *it++; Char c = *it++;
if (it != end && *it == c) { if (it != end && *it == c) {
out = write(out, basic_string_view<Char>(start, to_unsigned(it - start))); write(out, basic_string_view<Char>(start, to_unsigned(it - start)));
start = ++it; start = ++it;
continue; continue;
} }
out = write(out, basic_string_view<Char>(start, to_unsigned(it - 1 - start)));
write(out, basic_string_view<Char>(start, to_unsigned(it - 1 - start)));
auto specs = format_specs<Char>(); auto specs = format_specs<Char>();
specs.align = align::right; specs.align = align::right;
@ -469,16 +474,17 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
auto arg = get_arg(arg_index); auto arg = get_arg(arg_index);
// For d, i, o, u, x, and X conversion specifiers, if a precision is // For d, i, o, u, x, and X conversion specifiers, if a precision is
// specified, the '0' flag is ignored // specified, the '0' flag is ignored
if (specs.precision >= 0 && arg.is_integral()) if (specs.precision >= 0 && arg.is_integral()) {
specs.fill[0] = // Ignore '0' for non-numeric types or if '-' present.
' '; // Ignore '0' flag for non-numeric types or if '-' present. specs.fill[0] = ' ';
}
if (specs.precision >= 0 && arg.type() == type::cstring_type) { if (specs.precision >= 0 && arg.type() == type::cstring_type) {
auto str = visit_format_arg(get_cstring<Char>(), arg); auto str = visit_format_arg(get_cstring<Char>(), arg);
auto str_end = str + specs.precision; auto str_end = str + specs.precision;
auto nul = std::find(str, str_end, Char()); auto nul = std::find(str, str_end, Char());
arg = make_arg<basic_printf_context<iterator, Char>>( auto sv = basic_string_view<Char>(
basic_string_view<Char>( str, to_unsigned(nul != str_end ? nul - str : specs.precision));
str, to_unsigned(nul != str_end ? nul - str : specs.precision))); arg = make_arg<basic_printf_context<Char>>(sv);
} }
if (specs.alt && visit_format_arg(is_zero_int(), arg)) specs.alt = false; if (specs.alt && visit_format_arg(is_zero_int(), arg)) specs.alt = false;
if (specs.fill[0] == '0') { if (specs.fill[0] == '0') {
@ -540,8 +546,7 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
type = 'd'; type = 'd';
break; break;
case 'c': case 'c':
visit_format_arg( visit_format_arg(char_converter<basic_printf_context<Char>>(arg), arg);
char_converter<basic_printf_context<iterator, Char>>(arg), arg);
break; break;
} }
} }
@ -552,19 +557,14 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
start = it; start = it;
// Format argument. // Format argument.
out = visit_format_arg( visit_format_arg(printf_arg_formatter<Char>(out, specs, context), arg);
printf_arg_formatter<iterator, Char>(out, specs, context), arg);
} }
write(out, basic_string_view<Char>(start, to_unsigned(it - start))); write(out, basic_string_view<Char>(start, to_unsigned(it - start)));
} }
FMT_END_DETAIL_NAMESPACE } // namespace detail
template <typename Char> using printf_context = basic_printf_context<char>;
using basic_printf_context_t = using wprintf_context = basic_printf_context<wchar_t>;
basic_printf_context<detail::buffer_appender<Char>, Char>;
using printf_context = basic_printf_context_t<char>;
using wprintf_context = basic_printf_context_t<wchar_t>;
using printf_args = basic_format_args<printf_context>; using printf_args = basic_format_args<printf_context>;
using wprintf_args = basic_format_args<wprintf_context>; using wprintf_args = basic_format_args<wprintf_context>;
@ -581,25 +581,20 @@ inline auto make_printf_args(const T&... args)
return {args...}; return {args...};
} }
/** // DEPRECATED!
\rst
Constructs an `~fmt::format_arg_store` object that contains references to
arguments and can be implicitly converted to `~fmt::wprintf_args`.
\endrst
*/
template <typename... T> template <typename... T>
inline auto make_wprintf_args(const T&... args) inline auto make_wprintf_args(const T&... args)
-> format_arg_store<wprintf_context, T...> { -> format_arg_store<wprintf_context, T...> {
return {args...}; return {args...};
} }
template <typename S, typename Char = char_t<S>> template <typename Char>
inline auto vsprintf( inline auto vsprintf(
const S& fmt, basic_string_view<Char> fmt,
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) basic_format_args<basic_printf_context<type_identity_t<Char>>> args)
-> std::basic_string<Char> { -> std::basic_string<Char> {
auto buf = basic_memory_buffer<Char>(); auto buf = basic_memory_buffer<Char>();
detail::vprintf(buf, detail::to_string_view(fmt), args); detail::vprintf(buf, fmt, args);
return to_string(buf); return to_string(buf);
} }
@ -615,18 +610,17 @@ inline auto vsprintf(
template <typename S, typename... T, template <typename S, typename... T,
typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>> typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
inline auto sprintf(const S& fmt, const T&... args) -> std::basic_string<Char> { inline auto sprintf(const S& fmt, const T&... args) -> std::basic_string<Char> {
using context = basic_printf_context_t<Char>;
return vsprintf(detail::to_string_view(fmt), return vsprintf(detail::to_string_view(fmt),
fmt::make_format_args<context>(args...)); fmt::make_format_args<basic_printf_context<Char>>(args...));
} }
template <typename S, typename Char = char_t<S>> template <typename Char>
inline auto vfprintf( inline auto vfprintf(
std::FILE* f, const S& fmt, std::FILE* f, basic_string_view<Char> fmt,
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) basic_format_args<basic_printf_context<type_identity_t<Char>>> args)
-> int { -> int {
auto buf = basic_memory_buffer<Char>(); auto buf = basic_memory_buffer<Char>();
detail::vprintf(buf, detail::to_string_view(fmt), args); detail::vprintf(buf, fmt, args);
size_t size = buf.size(); size_t size = buf.size();
return std::fwrite(buf.data(), sizeof(Char), size, f) < size return std::fwrite(buf.data(), sizeof(Char), size, f) < size
? -1 ? -1
@ -644,17 +638,16 @@ inline auto vfprintf(
*/ */
template <typename S, typename... T, typename Char = char_t<S>> template <typename S, typename... T, typename Char = char_t<S>>
inline auto fprintf(std::FILE* f, const S& fmt, const T&... args) -> int { inline auto fprintf(std::FILE* f, const S& fmt, const T&... args) -> int {
using context = basic_printf_context_t<Char>;
return vfprintf(f, detail::to_string_view(fmt), return vfprintf(f, detail::to_string_view(fmt),
fmt::make_format_args<context>(args...)); fmt::make_format_args<basic_printf_context<Char>>(args...));
} }
template <typename S, typename Char = char_t<S>> template <typename Char>
inline auto vprintf( FMT_DEPRECATED inline auto vprintf(
const S& fmt, basic_string_view<Char> fmt,
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) basic_format_args<basic_printf_context<type_identity_t<Char>>> args)
-> int { -> int {
return vfprintf(stdout, detail::to_string_view(fmt), args); return vfprintf(stdout, fmt, args);
} }
/** /**
@ -666,11 +659,14 @@ inline auto vprintf(
fmt::printf("Elapsed time: %.2f seconds", 1.23); fmt::printf("Elapsed time: %.2f seconds", 1.23);
\endrst \endrst
*/ */
template <typename S, typename... T, FMT_ENABLE_IF(detail::is_string<S>::value)> template <typename... T>
inline auto printf(const S& fmt, const T&... args) -> int { inline auto printf(string_view fmt, const T&... args) -> int {
return vprintf( return vfprintf(stdout, fmt, make_printf_args(args...));
detail::to_string_view(fmt), }
fmt::make_format_args<basic_printf_context_t<char_t<S>>>(args...)); template <typename... T>
FMT_DEPRECATED inline auto printf(basic_string_view<wchar_t> fmt,
const T&... args) -> int {
return vfprintf(stdout, fmt, make_wprintf_args(args...));
} }
FMT_END_EXPORT FMT_END_EXPORT

View File

@ -1,13 +1,9 @@
// Formatting library for C++ - experimental range support // Formatting library for C++ - range and tuple support
// //
// Copyright (c) 2012 - present, Victor Zverovich // Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
// All rights reserved. // All rights reserved.
// //
// For the license information refer to format.h. // For the license information refer to format.h.
//
// Copyright (c) 2018 - present, Remotion (Igor Schulz)
// All Rights Reserved
// {fmt} support for ranges, containers and types tuple interface.
#ifndef FMT_RANGES_H_ #ifndef FMT_RANGES_H_
#define FMT_RANGES_H_ #define FMT_RANGES_H_
@ -187,7 +183,7 @@ template <size_t N> using make_index_sequence = std::make_index_sequence<N>;
template <typename T, T... N> struct integer_sequence { template <typename T, T... N> struct integer_sequence {
using value_type = T; using value_type = T;
static FMT_CONSTEXPR size_t size() { return sizeof...(N); } static FMT_CONSTEXPR auto size() -> size_t { return sizeof...(N); }
}; };
template <size_t... N> using index_sequence = integer_sequence<size_t, N...>; template <size_t... N> using index_sequence = integer_sequence<size_t, N...>;
@ -211,15 +207,15 @@ class is_tuple_formattable_ {
}; };
template <typename T, typename C> class is_tuple_formattable_<T, C, true> { template <typename T, typename C> class is_tuple_formattable_<T, C, true> {
template <std::size_t... Is> template <std::size_t... Is>
static std::true_type check2(index_sequence<Is...>, static auto check2(index_sequence<Is...>,
integer_sequence<bool, (Is == Is)...>); integer_sequence<bool, (Is == Is)...>) -> std::true_type;
static std::false_type check2(...); static auto check2(...) -> std::false_type;
template <std::size_t... Is> template <std::size_t... Is>
static decltype(check2( static auto check(index_sequence<Is...>) -> decltype(check2(
index_sequence<Is...>{}, index_sequence<Is...>{},
integer_sequence< integer_sequence<bool,
bool, (is_formattable<typename std::tuple_element<Is, T>::type, (is_formattable<typename std::tuple_element<Is, T>::type,
C>::value)...>{})) check(index_sequence<Is...>); C>::value)...>{}));
public: public:
static constexpr const bool value = static constexpr const bool value =
@ -421,6 +417,12 @@ struct is_formattable_delayed
#endif #endif
} // namespace detail } // namespace detail
template <typename...> struct conjunction : std::true_type {};
template <typename P> struct conjunction<P> : P {};
template <typename P1, typename... Pn>
struct conjunction<P1, Pn...>
: conditional_t<bool(P1::value), conjunction<Pn...>, P1> {};
template <typename T, typename Char, typename Enable = void> template <typename T, typename Char, typename Enable = void>
struct range_formatter; struct range_formatter;
@ -486,7 +488,8 @@ struct range_formatter<
for (; it != end; ++it) { for (; it != end; ++it) {
if (i > 0) out = detail::copy_str<Char>(separator_, out); if (i > 0) out = detail::copy_str<Char>(separator_, out);
ctx.advance_to(out); ctx.advance_to(out);
out = underlying_.format(mapper.map(*it), ctx); auto&& item = *it;
out = underlying_.format(mapper.map(item), ctx);
++i; ++i;
} }
out = detail::copy_str<Char>(closing_bracket_, out); out = detail::copy_str<Char>(closing_bracket_, out);
@ -668,8 +671,11 @@ template <typename Container> struct all {
} // namespace detail } // namespace detail
template <typename T, typename Char> template <typename T, typename Char>
struct formatter<T, Char, struct formatter<
enable_if_t<detail::is_container_adaptor_like<T>::value>> T, Char,
enable_if_t<conjunction<detail::is_container_adaptor_like<T>,
bool_constant<range_format_kind<T, Char>::value ==
range_format::disabled>>::value>>
: formatter<detail::all<typename T::container_type>, Char> { : formatter<detail::all<typename T::container_type>, Char> {
using all = detail::all<typename T::container_type>; using all = detail::all<typename T::container_type>;
template <typename FormatContext> template <typename FormatContext>

View File

@ -8,6 +8,8 @@
#ifndef FMT_STD_H_ #ifndef FMT_STD_H_
#define FMT_STD_H_ #define FMT_STD_H_
#include <atomic>
#include <bitset>
#include <cstdlib> #include <cstdlib>
#include <exception> #include <exception>
#include <memory> #include <memory>
@ -15,7 +17,9 @@
#include <type_traits> #include <type_traits>
#include <typeinfo> #include <typeinfo>
#include <utility> #include <utility>
#include <vector>
#include "format.h"
#include "ostream.h" #include "ostream.h"
#if FMT_HAS_INCLUDE(<version>) #if FMT_HAS_INCLUDE(<version>)
@ -34,6 +38,10 @@
# endif # endif
#endif #endif
#if FMT_CPLUSPLUS > 201703L && FMT_HAS_INCLUDE(<source_location>)
# include <source_location>
#endif
// GCC 4 does not support FMT_HAS_INCLUDE. // GCC 4 does not support FMT_HAS_INCLUDE.
#if FMT_HAS_INCLUDE(<cxxabi.h>) || defined(__GLIBCXX__) #if FMT_HAS_INCLUDE(<cxxabi.h>) || defined(__GLIBCXX__)
# include <cxxabi.h> # include <cxxabi.h>
@ -44,67 +52,155 @@
# endif # endif
#endif #endif
#ifdef __cpp_lib_filesystem // Check if typeid is available.
#ifndef FMT_USE_TYPEID
// __RTTI is for EDG compilers. In MSVC typeid is available without RTTI.
# if defined(__GXX_RTTI) || FMT_HAS_FEATURE(cxx_rtti) || FMT_MSC_VERSION || \
defined(__INTEL_RTTI__) || defined(__RTTI)
# define FMT_USE_TYPEID 1
# else
# define FMT_USE_TYPEID 0
# endif
#endif
// For older Xcode versions, __cpp_lib_xxx flags are inaccurately defined.
#ifndef FMT_CPP_LIB_FILESYSTEM
# ifdef __cpp_lib_filesystem
# define FMT_CPP_LIB_FILESYSTEM __cpp_lib_filesystem
# else
# define FMT_CPP_LIB_FILESYSTEM 0
# endif
#endif
#ifndef FMT_CPP_LIB_VARIANT
# ifdef __cpp_lib_variant
# define FMT_CPP_LIB_VARIANT __cpp_lib_variant
# else
# define FMT_CPP_LIB_VARIANT 0
# endif
#endif
#if FMT_CPP_LIB_FILESYSTEM
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
namespace detail { namespace detail {
template <typename Char> template <typename Char, typename PathChar>
auto get_path_string(const std::filesystem::path& p,
const std::basic_string<PathChar>& native) {
if constexpr (std::is_same_v<Char, char> && std::is_same_v<PathChar, wchar_t>)
return to_utf8<wchar_t>(native, to_utf8_error_policy::replace);
else
return p.string<Char>();
}
template <typename Char, typename PathChar>
void write_escaped_path(basic_memory_buffer<Char>& quoted, void write_escaped_path(basic_memory_buffer<Char>& quoted,
const std::filesystem::path& p) { const std::filesystem::path& p,
write_escaped_string<Char>(std::back_inserter(quoted), p.string<Char>()); const std::basic_string<PathChar>& native) {
} if constexpr (std::is_same_v<Char, char> &&
# ifdef _WIN32 std::is_same_v<PathChar, wchar_t>) {
template <> auto buf = basic_memory_buffer<wchar_t>();
inline void write_escaped_path<char>(memory_buffer& quoted, write_escaped_string<wchar_t>(std::back_inserter(buf), native);
const std::filesystem::path& p) { bool valid = to_utf8<wchar_t>::convert(quoted, {buf.data(), buf.size()});
auto buf = basic_memory_buffer<wchar_t>(); FMT_ASSERT(valid, "invalid utf16");
write_escaped_string<wchar_t>(std::back_inserter(buf), p.native()); } else if constexpr (std::is_same_v<Char, PathChar>) {
// Convert UTF-16 to UTF-8. write_escaped_string<std::filesystem::path::value_type>(
if (!unicode_to_utf8<wchar_t>::convert(quoted, {buf.data(), buf.size()})) std::back_inserter(quoted), native);
FMT_THROW(std::runtime_error("invalid utf16")); } else {
} write_escaped_string<Char>(std::back_inserter(quoted), p.string<Char>());
# endif }
template <>
inline void write_escaped_path<std::filesystem::path::value_type>(
basic_memory_buffer<std::filesystem::path::value_type>& quoted,
const std::filesystem::path& p) {
write_escaped_string<std::filesystem::path::value_type>(
std::back_inserter(quoted), p.native());
} }
} // namespace detail } // namespace detail
FMT_MODULE_EXPORT FMT_EXPORT
template <typename Char> template <typename Char> struct formatter<std::filesystem::path, Char> {
struct formatter<std::filesystem::path, Char> private:
: formatter<basic_string_view<Char>> { format_specs<Char> specs_;
detail::arg_ref<Char> width_ref_;
bool debug_ = false;
char path_type_ = 0;
public:
FMT_CONSTEXPR void set_debug_format(bool set = true) { debug_ = set; }
template <typename ParseContext> FMT_CONSTEXPR auto parse(ParseContext& ctx) { template <typename ParseContext> FMT_CONSTEXPR auto parse(ParseContext& ctx) {
auto out = formatter<basic_string_view<Char>>::parse(ctx); auto it = ctx.begin(), end = ctx.end();
this->set_debug_format(false); if (it == end) return it;
return out;
it = detail::parse_align(it, end, specs_);
if (it == end) return it;
it = detail::parse_dynamic_spec(it, end, specs_.width, width_ref_, ctx);
if (it != end && *it == '?') {
debug_ = true;
++it;
}
if (it != end && (*it == 'g')) path_type_ = *it++;
return it;
} }
template <typename FormatContext> template <typename FormatContext>
auto format(const std::filesystem::path& p, FormatContext& ctx) const -> auto format(const std::filesystem::path& p, FormatContext& ctx) const {
typename FormatContext::iterator { auto specs = specs_;
# ifdef _WIN32
auto path_string = !path_type_ ? p.native() : p.generic_wstring();
# else
auto path_string = !path_type_ ? p.native() : p.generic_string();
# endif
detail::handle_dynamic_spec<detail::width_checker>(specs.width, width_ref_,
ctx);
if (!debug_) {
auto s = detail::get_path_string<Char>(p, path_string);
return detail::write(ctx.out(), basic_string_view<Char>(s), specs);
}
auto quoted = basic_memory_buffer<Char>(); auto quoted = basic_memory_buffer<Char>();
detail::write_escaped_path(quoted, p); detail::write_escaped_path(quoted, p, path_string);
return formatter<basic_string_view<Char>>::format( return detail::write(ctx.out(),
basic_string_view<Char>(quoted.data(), quoted.size()), ctx); basic_string_view<Char>(quoted.data(), quoted.size()),
specs);
} }
}; };
FMT_END_NAMESPACE FMT_END_NAMESPACE
#endif #endif // FMT_CPP_LIB_FILESYSTEM
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
FMT_MODULE_EXPORT FMT_EXPORT
template <std::size_t N, typename Char>
struct formatter<std::bitset<N>, Char> : nested_formatter<string_view> {
private:
// Functor because C++11 doesn't support generic lambdas.
struct writer {
const std::bitset<N>& bs;
template <typename OutputIt>
FMT_CONSTEXPR auto operator()(OutputIt out) -> OutputIt {
for (auto pos = N; pos > 0; --pos) {
out = detail::write<Char>(out, bs[pos - 1] ? Char('1') : Char('0'));
}
return out;
}
};
public:
template <typename FormatContext>
auto format(const std::bitset<N>& bs, FormatContext& ctx) const
-> decltype(ctx.out()) {
return write_padded(ctx, writer{bs});
}
};
FMT_EXPORT
template <typename Char> template <typename Char>
struct formatter<std::thread::id, Char> : basic_ostream_formatter<Char> {}; struct formatter<std::thread::id, Char> : basic_ostream_formatter<Char> {};
FMT_END_NAMESPACE FMT_END_NAMESPACE
#ifdef __cpp_lib_optional #ifdef __cpp_lib_optional
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
FMT_MODULE_EXPORT FMT_EXPORT
template <typename T, typename Char> template <typename T, typename Char>
struct formatter<std::optional<T>, Char, struct formatter<std::optional<T>, Char,
std::enable_if_t<is_formattable<T, Char>::value>> { std::enable_if_t<is_formattable<T, Char>::value>> {
@ -132,7 +228,7 @@ struct formatter<std::optional<T>, Char,
} }
template <typename FormatContext> template <typename FormatContext>
auto format(std::optional<T> const& opt, FormatContext& ctx) const auto format(const std::optional<T>& opt, FormatContext& ctx) const
-> decltype(ctx.out()) { -> decltype(ctx.out()) {
if (!opt) return detail::write<Char>(ctx.out(), none); if (!opt) return detail::write<Char>(ctx.out(), none);
@ -146,24 +242,33 @@ struct formatter<std::optional<T>, Char,
FMT_END_NAMESPACE FMT_END_NAMESPACE
#endif // __cpp_lib_optional #endif // __cpp_lib_optional
#ifdef __cpp_lib_variant #ifdef __cpp_lib_source_location
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
FMT_MODULE_EXPORT FMT_EXPORT
template <typename Char> struct formatter<std::monostate, Char> { template <> struct formatter<std::source_location> {
template <typename ParseContext> template <typename ParseContext> FMT_CONSTEXPR auto parse(ParseContext& ctx) {
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin(); return ctx.begin();
} }
template <typename FormatContext> template <typename FormatContext>
auto format(const std::monostate&, FormatContext& ctx) const auto format(const std::source_location& loc, FormatContext& ctx) const
-> decltype(ctx.out()) { -> decltype(ctx.out()) {
auto out = ctx.out(); auto out = ctx.out();
out = detail::write<Char>(out, "monostate"); out = detail::write(out, loc.file_name());
out = detail::write(out, ':');
out = detail::write<char>(out, loc.line());
out = detail::write(out, ':');
out = detail::write<char>(out, loc.column());
out = detail::write(out, ": ");
out = detail::write(out, loc.function_name());
return out; return out;
} }
}; };
FMT_END_NAMESPACE
#endif
#if FMT_CPP_LIB_VARIANT
FMT_BEGIN_NAMESPACE
namespace detail { namespace detail {
template <typename T> template <typename T>
@ -197,6 +302,7 @@ auto write_variant_alternative(OutputIt out, const T& v) -> OutputIt {
} }
} // namespace detail } // namespace detail
template <typename T> struct is_variant_like { template <typename T> struct is_variant_like {
static constexpr const bool value = detail::is_variant_like_<T>::value; static constexpr const bool value = detail::is_variant_like_<T>::value;
}; };
@ -206,7 +312,21 @@ template <typename T, typename C> struct is_variant_formattable {
detail::is_variant_formattable_<T, C>::value; detail::is_variant_formattable_<T, C>::value;
}; };
FMT_MODULE_EXPORT FMT_EXPORT
template <typename Char> struct formatter<std::monostate, Char> {
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
template <typename FormatContext>
auto format(const std::monostate&, FormatContext& ctx) const
-> decltype(ctx.out()) {
return detail::write<Char>(ctx.out(), "monostate");
}
};
FMT_EXPORT
template <typename Variant, typename Char> template <typename Variant, typename Char>
struct formatter< struct formatter<
Variant, Char, Variant, Char,
@ -223,13 +343,14 @@ struct formatter<
auto out = ctx.out(); auto out = ctx.out();
out = detail::write<Char>(out, "variant("); out = detail::write<Char>(out, "variant(");
try { FMT_TRY {
std::visit( std::visit(
[&](const auto& v) { [&](const auto& v) {
out = detail::write_variant_alternative<Char>(out, v); out = detail::write_variant_alternative<Char>(out, v);
}, },
value); value);
} catch (const std::bad_variant_access&) { }
FMT_CATCH(const std::bad_variant_access&) {
detail::write<Char>(out, "valueless by exception"); detail::write<Char>(out, "valueless by exception");
} }
*out++ = ')'; *out++ = ')';
@ -237,10 +358,10 @@ struct formatter<
} }
}; };
FMT_END_NAMESPACE FMT_END_NAMESPACE
#endif // __cpp_lib_variant #endif // FMT_CPP_LIB_VARIANT
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
FMT_MODULE_EXPORT FMT_EXPORT
template <typename Char> struct formatter<std::error_code, Char> { template <typename Char> struct formatter<std::error_code, Char> {
template <typename ParseContext> template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
@ -258,10 +379,10 @@ template <typename Char> struct formatter<std::error_code, Char> {
} }
}; };
FMT_MODULE_EXPORT FMT_EXPORT
template <typename T, typename Char> template <typename T, typename Char>
struct formatter< struct formatter<
T, Char, T, Char, // DEPRECATED! Mixing code unit types.
typename std::enable_if<std::is_base_of<std::exception, T>::value>::type> { typename std::enable_if<std::is_base_of<std::exception, T>::value>::type> {
private: private:
bool with_typename_ = false; bool with_typename_ = false;
@ -274,7 +395,7 @@ struct formatter<
if (it == end || *it == '}') return it; if (it == end || *it == '}') return it;
if (*it == 't') { if (*it == 't') {
++it; ++it;
with_typename_ = true; with_typename_ = FMT_USE_TYPEID != 0;
} }
return it; return it;
} }
@ -287,11 +408,12 @@ struct formatter<
if (!with_typename_) if (!with_typename_)
return detail::write_bytes(out, string_view(ex.what()), spec); return detail::write_bytes(out, string_view(ex.what()), spec);
#if FMT_USE_TYPEID
const std::type_info& ti = typeid(ex); const std::type_info& ti = typeid(ex);
#ifdef FMT_HAS_ABI_CXA_DEMANGLE # ifdef FMT_HAS_ABI_CXA_DEMANGLE
int status = 0; int status = 0;
std::size_t size = 0; std::size_t size = 0;
std::unique_ptr<char, decltype(&std::free)> demangled_name_ptr( std::unique_ptr<char, void (*)(void*)> demangled_name_ptr(
abi::__cxa_demangle(ti.name(), nullptr, &size, &status), &std::free); abi::__cxa_demangle(ti.name(), nullptr, &size, &status), &std::free);
string_view demangled_name_view; string_view demangled_name_view;
@ -327,23 +449,89 @@ struct formatter<
demangled_name_view = string_view(ti.name()); demangled_name_view = string_view(ti.name());
} }
out = detail::write_bytes(out, demangled_name_view, spec); out = detail::write_bytes(out, demangled_name_view, spec);
#elif FMT_MSC_VERSION # elif FMT_MSC_VERSION
string_view demangled_name_view(ti.name()); string_view demangled_name_view(ti.name());
if (demangled_name_view.starts_with("class ")) if (demangled_name_view.starts_with("class "))
demangled_name_view.remove_prefix(6); demangled_name_view.remove_prefix(6);
else if (demangled_name_view.starts_with("struct ")) else if (demangled_name_view.starts_with("struct "))
demangled_name_view.remove_prefix(7); demangled_name_view.remove_prefix(7);
out = detail::write_bytes(out, demangled_name_view, spec); out = detail::write_bytes(out, demangled_name_view, spec);
#else # else
out = detail::write_bytes(out, string_view(ti.name()), spec); out = detail::write_bytes(out, string_view(ti.name()), spec);
# endif
*out++ = ':';
*out++ = ' ';
return detail::write_bytes(out, string_view(ex.what()), spec);
#endif #endif
out = detail::write<Char>(out, Char(':'));
out = detail::write<Char>(out, Char(' '));
out = detail::write_bytes(out, string_view(ex.what()), spec);
return out;
} }
}; };
FMT_END_NAMESPACE
namespace detail {
template <typename T, typename Enable = void>
struct has_flip : std::false_type {};
template <typename T>
struct has_flip<T, void_t<decltype(std::declval<T>().flip())>>
: std::true_type {};
template <typename T> struct is_bit_reference_like {
static constexpr const bool value =
std::is_convertible<T, bool>::value &&
std::is_nothrow_assignable<T, bool>::value && has_flip<T>::value;
};
#ifdef _LIBCPP_VERSION
// Workaround for libc++ incompatibility with C++ standard.
// According to the Standard, `bitset::operator[] const` returns bool.
template <typename C>
struct is_bit_reference_like<std::__bit_const_reference<C>> {
static constexpr const bool value = true;
};
#endif
} // namespace detail
// We can't use std::vector<bool, Allocator>::reference and
// std::bitset<N>::reference because the compiler can't deduce Allocator and N
// in partial specialization.
FMT_EXPORT
template <typename BitRef, typename Char>
struct formatter<BitRef, Char,
enable_if_t<detail::is_bit_reference_like<BitRef>::value>>
: formatter<bool, Char> {
template <typename FormatContext>
FMT_CONSTEXPR auto format(const BitRef& v, FormatContext& ctx) const
-> decltype(ctx.out()) {
return formatter<bool, Char>::format(v, ctx);
}
};
FMT_EXPORT
template <typename T, typename Char>
struct formatter<std::atomic<T>, Char,
enable_if_t<is_formattable<T, Char>::value>>
: formatter<T, Char> {
template <typename FormatContext>
auto format(const std::atomic<T>& v, FormatContext& ctx) const
-> decltype(ctx.out()) {
return formatter<T, Char>::format(v.load(), ctx);
}
};
#ifdef __cpp_lib_atomic_flag_test
FMT_EXPORT
template <typename Char>
struct formatter<std::atomic_flag, Char> : formatter<bool, Char> {
template <typename FormatContext>
auto format(const std::atomic_flag& v, FormatContext& ctx) const
-> decltype(ctx.out()) {
return formatter<bool, Char>::format(v.test(), ctx);
}
};
#endif // __cpp_lib_atomic_flag_test
FMT_END_NAMESPACE
#endif // FMT_STD_H_ #endif // FMT_STD_H_

504
source/extern/malloc.c vendored Normal file
View File

@ -0,0 +1,504 @@
/* malloc.c - Memory allocator - Public Domain - 2016 Mattias Jansson
*
* This library provides a cross-platform lock free thread caching malloc implementation in C11.
* The latest source code is always available at
*
* https://github.com/mjansson/rpmalloc
*
* This library is put in the public domain; you can redistribute it and/or modify it without any restrictions.
*
*/
//
// This file provides overrides for the standard library malloc entry points for C and new/delete operators for C++
// It also provides automatic initialization/finalization of process and threads
//
#if defined(__TINYC__)
#include <sys/types.h>
#endif
#ifndef ARCH_64BIT
# if defined(__LLP64__) || defined(__LP64__) || defined(_WIN64)
# define ARCH_64BIT 1
# else
# define ARCH_64BIT 0
_Static_assert(sizeof(size_t) == 4, "Data type size mismatch");
_Static_assert(sizeof(void*) == 4, "Data type size mismatch");
# endif
#endif
#if (defined(__GNUC__) || defined(__clang__))
#pragma GCC visibility push(default)
#endif
#define USE_IMPLEMENT 1
#define USE_INTERPOSE 0
#define USE_ALIAS 0
#if defined(__APPLE__)
#undef USE_INTERPOSE
#define USE_INTERPOSE 1
typedef struct interpose_t {
void* new_func;
void* orig_func;
} interpose_t;
#define MAC_INTERPOSE_PAIR(newf, oldf) { (void*)newf, (void*)oldf }
#define MAC_INTERPOSE_SINGLE(newf, oldf) \
__attribute__((used)) static const interpose_t macinterpose##newf##oldf \
__attribute__ ((section("__DATA, __interpose"))) = MAC_INTERPOSE_PAIR(newf, oldf)
#endif
#if !defined(_WIN32) && !defined(__APPLE__)
#undef USE_IMPLEMENT
#undef USE_ALIAS
#define USE_IMPLEMENT 0
#define USE_ALIAS 1
#endif
#ifdef _MSC_VER
#pragma warning (disable : 4100)
#undef malloc
#undef free
#undef calloc
#define RPMALLOC_RESTRICT __declspec(restrict)
#else
#define RPMALLOC_RESTRICT
#endif
#if ENABLE_OVERRIDE
typedef struct rp_nothrow_t { int __dummy; } rp_nothrow_t;
#if USE_IMPLEMENT
extern inline RPMALLOC_RESTRICT void* RPMALLOC_CDECL malloc(size_t size) { return rpmalloc(size); }
extern inline RPMALLOC_RESTRICT void* RPMALLOC_CDECL calloc(size_t count, size_t size) { return rpcalloc(count, size); }
extern inline RPMALLOC_RESTRICT void* RPMALLOC_CDECL realloc(void* ptr, size_t size) { return rprealloc(ptr, size); }
extern inline void* RPMALLOC_CDECL reallocf(void* ptr, size_t size) { return rprealloc(ptr, size); }
extern inline void* RPMALLOC_CDECL aligned_alloc(size_t alignment, size_t size) { return rpaligned_alloc(alignment, size); }
extern inline void* RPMALLOC_CDECL memalign(size_t alignment, size_t size) { return rpmemalign(alignment, size); }
extern inline int RPMALLOC_CDECL posix_memalign(void** memptr, size_t alignment, size_t size) { return rpposix_memalign(memptr, alignment, size); }
extern inline void RPMALLOC_CDECL free(void* ptr) { rpfree(ptr); }
extern inline void RPMALLOC_CDECL cfree(void* ptr) { rpfree(ptr); }
extern inline size_t RPMALLOC_CDECL malloc_usable_size(void* ptr) { return rpmalloc_usable_size(ptr); }
extern inline size_t RPMALLOC_CDECL malloc_size(void* ptr) { return rpmalloc_usable_size(ptr); }
#ifdef _WIN32
extern inline RPMALLOC_RESTRICT void* RPMALLOC_CDECL _malloc_base(size_t size) { return rpmalloc(size); }
extern inline void RPMALLOC_CDECL _free_base(void* ptr) { rpfree(ptr); }
extern inline RPMALLOC_RESTRICT void* RPMALLOC_CDECL _calloc_base(size_t count, size_t size) { return rpcalloc(count, size); }
extern inline size_t RPMALLOC_CDECL _msize(void* ptr) { return rpmalloc_usable_size(ptr); }
extern inline size_t RPMALLOC_CDECL _msize_base(void* ptr) { return rpmalloc_usable_size(ptr); }
extern inline RPMALLOC_RESTRICT void* RPMALLOC_CDECL _realloc_base(void* ptr, size_t size) { return rprealloc(ptr, size); }
#endif
#ifdef _WIN32
// For Windows, #include <rpnew.h> in one source file to get the C++ operator overrides implemented in your module
#else
// Overload the C++ operators using the mangled names (https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling)
// operators delete and delete[]
#define RPDEFVIS __attribute__((visibility("default")))
extern void _ZdlPv(void* p); void RPDEFVIS _ZdlPv(void* p) { rpfree(p); }
extern void _ZdaPv(void* p); void RPDEFVIS _ZdaPv(void* p) { rpfree(p); }
#if ARCH_64BIT
// 64-bit operators new and new[], normal and aligned
extern void* _Znwm(uint64_t size); void* RPDEFVIS _Znwm(uint64_t size) { return rpmalloc(size); }
extern void* _Znam(uint64_t size); void* RPDEFVIS _Znam(uint64_t size) { return rpmalloc(size); }
extern void* _Znwmm(uint64_t size, uint64_t align); void* RPDEFVIS _Znwmm(uint64_t size, uint64_t align) { return rpaligned_alloc(align, size); }
extern void* _Znamm(uint64_t size, uint64_t align); void* RPDEFVIS _Znamm(uint64_t size, uint64_t align) { return rpaligned_alloc(align, size); }
extern void* _ZnwmSt11align_val_t(uint64_t size, uint64_t align); void* RPDEFVIS _ZnwmSt11align_val_t(uint64_t size, uint64_t align) { return rpaligned_alloc(align, size); }
extern void* _ZnamSt11align_val_t(uint64_t size, uint64_t align); void* RPDEFVIS _ZnamSt11align_val_t(uint64_t size, uint64_t align) { return rpaligned_alloc(align, size); }
extern void* _ZnwmRKSt9nothrow_t(uint64_t size, rp_nothrow_t t); void* RPDEFVIS _ZnwmRKSt9nothrow_t(uint64_t size, rp_nothrow_t t) { (void)sizeof(t); return rpmalloc(size); }
extern void* _ZnamRKSt9nothrow_t(uint64_t size, rp_nothrow_t t); void* RPDEFVIS _ZnamRKSt9nothrow_t(uint64_t size, rp_nothrow_t t) { (void)sizeof(t); return rpmalloc(size); }
extern void* _ZnwmSt11align_val_tRKSt9nothrow_t(uint64_t size, uint64_t align, rp_nothrow_t t); void* RPDEFVIS _ZnwmSt11align_val_tRKSt9nothrow_t(uint64_t size, uint64_t align, rp_nothrow_t t) { (void)sizeof(t); return rpaligned_alloc(align, size); }
extern void* _ZnamSt11align_val_tRKSt9nothrow_t(uint64_t size, uint64_t align, rp_nothrow_t t); void* RPDEFVIS _ZnamSt11align_val_tRKSt9nothrow_t(uint64_t size, uint64_t align, rp_nothrow_t t) { (void)sizeof(t); return rpaligned_alloc(align, size); }
// 64-bit operators sized delete and delete[], normal and aligned
extern void _ZdlPvm(void* p, uint64_t size); void RPDEFVIS _ZdlPvm(void* p, uint64_t size) { rpfree(p); (void)sizeof(size); }
extern void _ZdaPvm(void* p, uint64_t size); void RPDEFVIS _ZdaPvm(void* p, uint64_t size) { rpfree(p); (void)sizeof(size); }
extern void _ZdlPvSt11align_val_t(void* p, uint64_t align); void RPDEFVIS _ZdlPvSt11align_val_t(void* p, uint64_t align) { rpfree(p); (void)sizeof(align); }
extern void _ZdaPvSt11align_val_t(void* p, uint64_t align); void RPDEFVIS _ZdaPvSt11align_val_t(void* p, uint64_t align) { rpfree(p); (void)sizeof(align); }
extern void _ZdlPvmSt11align_val_t(void* p, uint64_t size, uint64_t align); void RPDEFVIS _ZdlPvmSt11align_val_t(void* p, uint64_t size, uint64_t align) { rpfree(p); (void)sizeof(size); (void)sizeof(align); }
extern void _ZdaPvmSt11align_val_t(void* p, uint64_t size, uint64_t align); void RPDEFVIS _ZdaPvmSt11align_val_t(void* p, uint64_t size, uint64_t align) { rpfree(p); (void)sizeof(size); (void)sizeof(align); }
#else
// 32-bit operators new and new[], normal and aligned
extern void* _Znwj(uint32_t size); void* RPDEFVIS _Znwj(uint32_t size) { return rpmalloc(size); }
extern void* _Znaj(uint32_t size); void* RPDEFVIS _Znaj(uint32_t size) { return rpmalloc(size); }
extern void* _Znwjj(uint32_t size, uint32_t align); void* RPDEFVIS _Znwjj(uint32_t size, uint32_t align) { return rpaligned_alloc(align, size); }
extern void* _Znajj(uint32_t size, uint32_t align); void* RPDEFVIS _Znajj(uint32_t size, uint32_t align) { return rpaligned_alloc(align, size); }
extern void* _ZnwjSt11align_val_t(size_t size, size_t align); void* RPDEFVIS _ZnwjSt11align_val_t(size_t size, size_t align) { return rpaligned_alloc(align, size); }
extern void* _ZnajSt11align_val_t(size_t size, size_t align); void* RPDEFVIS _ZnajSt11align_val_t(size_t size, size_t align) { return rpaligned_alloc(align, size); }
extern void* _ZnwjRKSt9nothrow_t(size_t size, rp_nothrow_t t); void* RPDEFVIS _ZnwjRKSt9nothrow_t(size_t size, rp_nothrow_t t) { (void)sizeof(t); return rpmalloc(size); }
extern void* _ZnajRKSt9nothrow_t(size_t size, rp_nothrow_t t); void* RPDEFVIS _ZnajRKSt9nothrow_t(size_t size, rp_nothrow_t t) { (void)sizeof(t); return rpmalloc(size); }
extern void* _ZnwjSt11align_val_tRKSt9nothrow_t(size_t size, size_t align, rp_nothrow_t t); void* RPDEFVIS _ZnwjSt11align_val_tRKSt9nothrow_t(size_t size, size_t align, rp_nothrow_t t) { (void)sizeof(t); return rpaligned_alloc(align, size); }
extern void* _ZnajSt11align_val_tRKSt9nothrow_t(size_t size, size_t align, rp_nothrow_t t); void* RPDEFVIS _ZnajSt11align_val_tRKSt9nothrow_t(size_t size, size_t align, rp_nothrow_t t) { (void)sizeof(t); return rpaligned_alloc(align, size); }
// 32-bit operators sized delete and delete[], normal and aligned
extern void _ZdlPvj(void* p, uint64_t size); void RPDEFVIS _ZdlPvj(void* p, uint64_t size) { rpfree(p); (void)sizeof(size); }
extern void _ZdaPvj(void* p, uint64_t size); void RPDEFVIS _ZdaPvj(void* p, uint64_t size) { rpfree(p); (void)sizeof(size); }
extern void _ZdlPvSt11align_val_t(void* p, uint32_t align); void RPDEFVIS _ZdlPvSt11align_val_t(void* p, uint64_t a) { rpfree(p); (void)sizeof(align); }
extern void _ZdaPvSt11align_val_t(void* p, uint32_t align); void RPDEFVIS _ZdaPvSt11align_val_t(void* p, uint64_t a) { rpfree(p); (void)sizeof(align); }
extern void _ZdlPvjSt11align_val_t(void* p, uint32_t size, uint32_t align); void RPDEFVIS _ZdlPvjSt11align_val_t(void* p, uint64_t size, uint64_t align) { rpfree(p); (void)sizeof(size); (void)sizeof(a); }
extern void _ZdaPvjSt11align_val_t(void* p, uint32_t size, uint32_t align); void RPDEFVIS _ZdaPvjSt11align_val_t(void* p, uint64_t size, uint64_t align) { rpfree(p); (void)sizeof(size); (void)sizeof(a); }
#endif
#endif
#endif
#if USE_INTERPOSE || USE_ALIAS
static void* rpmalloc_nothrow(size_t size, rp_nothrow_t t) { (void)sizeof(t); return rpmalloc(size); }
static void* rpaligned_alloc_reverse(size_t size, size_t align) { return rpaligned_alloc(align, size); }
static void* rpaligned_alloc_reverse_nothrow(size_t size, size_t align, rp_nothrow_t t) { (void)sizeof(t); return rpaligned_alloc(align, size); }
static void rpfree_size(void* p, size_t size) { (void)sizeof(size); rpfree(p); }
static void rpfree_aligned(void* p, size_t align) { (void)sizeof(align); rpfree(p); }
static void rpfree_size_aligned(void* p, size_t size, size_t align) { (void)sizeof(size); (void)sizeof(align); rpfree(p); }
#endif
#if USE_INTERPOSE
__attribute__((used)) static const interpose_t macinterpose_malloc[]
__attribute__ ((section("__DATA, __interpose"))) = {
//new and new[]
MAC_INTERPOSE_PAIR(rpmalloc, _Znwm),
MAC_INTERPOSE_PAIR(rpmalloc, _Znam),
MAC_INTERPOSE_PAIR(rpaligned_alloc_reverse, _Znwmm),
MAC_INTERPOSE_PAIR(rpaligned_alloc_reverse, _Znamm),
MAC_INTERPOSE_PAIR(rpmalloc_nothrow, _ZnwmRKSt9nothrow_t),
MAC_INTERPOSE_PAIR(rpmalloc_nothrow, _ZnamRKSt9nothrow_t),
MAC_INTERPOSE_PAIR(rpaligned_alloc_reverse, _ZnwmSt11align_val_t),
MAC_INTERPOSE_PAIR(rpaligned_alloc_reverse, _ZnamSt11align_val_t),
MAC_INTERPOSE_PAIR(rpaligned_alloc_reverse_nothrow, _ZnwmSt11align_val_tRKSt9nothrow_t),
MAC_INTERPOSE_PAIR(rpaligned_alloc_reverse_nothrow, _ZnamSt11align_val_tRKSt9nothrow_t),
//delete and delete[]
MAC_INTERPOSE_PAIR(rpfree, _ZdlPv),
MAC_INTERPOSE_PAIR(rpfree, _ZdaPv),
MAC_INTERPOSE_PAIR(rpfree_size, _ZdlPvm),
MAC_INTERPOSE_PAIR(rpfree_size, _ZdaPvm),
MAC_INTERPOSE_PAIR(rpfree_aligned, _ZdlPvSt11align_val_t),
MAC_INTERPOSE_PAIR(rpfree_aligned, _ZdaPvSt11align_val_t),
MAC_INTERPOSE_PAIR(rpfree_size_aligned, _ZdlPvmSt11align_val_t),
MAC_INTERPOSE_PAIR(rpfree_size_aligned, _ZdaPvmSt11align_val_t),
//libc entry points
MAC_INTERPOSE_PAIR(rpmalloc, malloc),
MAC_INTERPOSE_PAIR(rpmalloc, calloc),
MAC_INTERPOSE_PAIR(rprealloc, realloc),
MAC_INTERPOSE_PAIR(rprealloc, reallocf),
#if defined(__MAC_10_15) && __MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_15
MAC_INTERPOSE_PAIR(rpaligned_alloc, aligned_alloc),
#endif
MAC_INTERPOSE_PAIR(rpmemalign, memalign),
MAC_INTERPOSE_PAIR(rpposix_memalign, posix_memalign),
MAC_INTERPOSE_PAIR(rpfree, free),
MAC_INTERPOSE_PAIR(rpfree, cfree),
MAC_INTERPOSE_PAIR(rpmalloc_usable_size, malloc_usable_size),
MAC_INTERPOSE_PAIR(rpmalloc_usable_size, malloc_size)
};
#endif
#if USE_ALIAS
#define RPALIAS(fn) __attribute__((alias(#fn), used, visibility("default")));
// Alias the C++ operators using the mangled names (https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling)
// operators delete and delete[]
void _ZdlPv(void* p) RPALIAS(rpfree)
void _ZdaPv(void* p) RPALIAS(rpfree)
#if ARCH_64BIT
// 64-bit operators new and new[], normal and aligned
void* _Znwm(uint64_t size) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE(1) RPALIAS(rpmalloc)
void* _Znam(uint64_t size) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE(1) RPALIAS(rpmalloc)
void* _Znwmm(uint64_t size, uint64_t align) RPALIAS(rpaligned_alloc_reverse)
void* _Znamm(uint64_t size, uint64_t align) RPALIAS(rpaligned_alloc_reverse)
void* _ZnwmSt11align_val_t(size_t size, size_t align) RPALIAS(rpaligned_alloc_reverse)
void* _ZnamSt11align_val_t(size_t size, size_t align) RPALIAS(rpaligned_alloc_reverse)
void* _ZnwmRKSt9nothrow_t(size_t size, rp_nothrow_t t) RPALIAS(rpmalloc_nothrow)
void* _ZnamRKSt9nothrow_t(size_t size, rp_nothrow_t t) RPALIAS(rpmalloc_nothrow)
void* _ZnwmSt11align_val_tRKSt9nothrow_t(size_t size, size_t align, rp_nothrow_t t) RPALIAS(rpaligned_alloc_reverse_nothrow)
void* _ZnamSt11align_val_tRKSt9nothrow_t(size_t size, size_t align, rp_nothrow_t t) RPALIAS(rpaligned_alloc_reverse_nothrow)
// 64-bit operators delete and delete[], sized and aligned
void _ZdlPvm(void* p, size_t n) RPALIAS(rpfree_size)
void _ZdaPvm(void* p, size_t n) RPALIAS(rpfree_size)
void _ZdlPvSt11align_val_t(void* p, size_t a) RPALIAS(rpfree_aligned)
void _ZdaPvSt11align_val_t(void* p, size_t a) RPALIAS(rpfree_aligned)
void _ZdlPvmSt11align_val_t(void* p, size_t n, size_t a) RPALIAS(rpfree_size_aligned)
void _ZdaPvmSt11align_val_t(void* p, size_t n, size_t a) RPALIAS(rpfree_size_aligned)
#else
// 32-bit operators new and new[], normal and aligned
void* _Znwj(uint32_t size) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE(1) RPALIAS(rpmalloc)
void* _Znaj(uint32_t size) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE(1) RPALIAS(rpmalloc)
void* _Znwjj(uint32_t size, uint32_t align) RPALIAS(rpaligned_alloc_reverse)
void* _Znajj(uint32_t size, uint32_t align) RPALIAS(rpaligned_alloc_reverse)
void* _ZnwjSt11align_val_t(size_t size, size_t align) RPALIAS(rpaligned_alloc_reverse)
void* _ZnajSt11align_val_t(size_t size, size_t align) RPALIAS(rpaligned_alloc_reverse)
void* _ZnwjRKSt9nothrow_t(size_t size, rp_nothrow_t t) RPALIAS(rpmalloc_nothrow)
void* _ZnajRKSt9nothrow_t(size_t size, rp_nothrow_t t) RPALIAS(rpmalloc_nothrow)
void* _ZnwjSt11align_val_tRKSt9nothrow_t(size_t size, size_t align, rp_nothrow_t t) RPALIAS(rpaligned_alloc_reverse_nothrow)
void* _ZnajSt11align_val_tRKSt9nothrow_t(size_t size, size_t align, rp_nothrow_t t) RPALIAS(rpaligned_alloc_reverse_nothrow)
// 32-bit operators delete and delete[], sized and aligned
void _ZdlPvj(void* p, size_t n) RPALIAS(rpfree_size)
void _ZdaPvj(void* p, size_t n) RPALIAS(rpfree_size)
void _ZdlPvSt11align_val_t(void* p, size_t a) RPALIAS(rpfree_aligned)
void _ZdaPvSt11align_val_t(void* p, size_t a) RPALIAS(rpfree_aligned)
void _ZdlPvjSt11align_val_t(void* p, size_t n, size_t a) RPALIAS(rpfree_size_aligned)
void _ZdaPvjSt11align_val_t(void* p, size_t n, size_t a) RPALIAS(rpfree_size_aligned)
#endif
void* malloc(size_t size) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE(1) RPALIAS(rpmalloc)
void* calloc(size_t count, size_t size) RPALIAS(rpcalloc)
void* realloc(void* ptr, size_t size) RPALIAS(rprealloc)
void* reallocf(void* ptr, size_t size) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE(2) RPALIAS(rprealloc)
void* aligned_alloc(size_t alignment, size_t size) RPALIAS(rpaligned_alloc)
void* memalign(size_t alignment, size_t size) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE(2) RPALIAS(rpmemalign)
int posix_memalign(void** memptr, size_t alignment, size_t size) RPALIAS(rpposix_memalign)
void free(void* ptr) RPALIAS(rpfree)
void cfree(void* ptr) RPALIAS(rpfree)
#if defined(__ANDROID__) || defined(__FreeBSD__)
size_t malloc_usable_size(const void* ptr) RPALIAS(rpmalloc_usable_size)
#else
size_t malloc_usable_size(void* ptr) RPALIAS(rpmalloc_usable_size)
#endif
size_t malloc_size(void* ptr) RPALIAS(rpmalloc_usable_size)
#endif
static inline size_t
_rpmalloc_page_size(void) {
return _memory_page_size;
}
extern void* RPMALLOC_CDECL
reallocarray(void* ptr, size_t count, size_t size);
extern void* RPMALLOC_CDECL
reallocarray(void* ptr, size_t count, size_t size) {
size_t total;
#if ENABLE_VALIDATE_ARGS
#ifdef _MSC_VER
int err = SizeTMult(count, size, &total);
if ((err != S_OK) || (total >= MAX_ALLOC_SIZE)) {
errno = EINVAL;
return 0;
}
#else
int err = __builtin_umull_overflow(count, size, &total);
if (err || (total >= MAX_ALLOC_SIZE)) {
errno = EINVAL;
return 0;
}
#endif
#else
total = count * size;
#endif
return realloc(ptr, total);
}
extern inline void* RPMALLOC_CDECL
valloc(size_t size) {
get_thread_heap();
return rpaligned_alloc(_rpmalloc_page_size(), size);
}
extern inline void* RPMALLOC_CDECL
pvalloc(size_t size) {
get_thread_heap();
const size_t page_size = _rpmalloc_page_size();
const size_t aligned_size = ((size + page_size - 1) / page_size) * page_size;
#if ENABLE_VALIDATE_ARGS
if (aligned_size < size) {
errno = EINVAL;
return 0;
}
#endif
return rpaligned_alloc(_rpmalloc_page_size(), aligned_size);
}
#endif // ENABLE_OVERRIDE
#if ENABLE_PRELOAD
#ifdef _WIN32
#if defined(BUILD_DYNAMIC_LINK) && BUILD_DYNAMIC_LINK
extern __declspec(dllexport) BOOL WINAPI
DllMain(HINSTANCE instance, DWORD reason, LPVOID reserved);
extern __declspec(dllexport) BOOL WINAPI
DllMain(HINSTANCE instance, DWORD reason, LPVOID reserved) {
(void)sizeof(reserved);
(void)sizeof(instance);
if (reason == DLL_PROCESS_ATTACH)
rpmalloc_initialize();
else if (reason == DLL_PROCESS_DETACH)
rpmalloc_finalize();
else if (reason == DLL_THREAD_ATTACH)
rpmalloc_thread_initialize();
else if (reason == DLL_THREAD_DETACH)
rpmalloc_thread_finalize(1);
return TRUE;
}
//end BUILD_DYNAMIC_LINK
#else
extern void
_global_rpmalloc_init(void) {
rpmalloc_set_main_thread();
rpmalloc_initialize();
}
#if defined(__clang__) || defined(__GNUC__)
static void __attribute__((constructor))
initializer(void) {
_global_rpmalloc_init();
}
#elif defined(_MSC_VER)
static int
_global_rpmalloc_xib(void) {
_global_rpmalloc_init();
return 0;
}
#pragma section(".CRT$XIB",read)
__declspec(allocate(".CRT$XIB")) void (*_rpmalloc_module_init)(void) = _global_rpmalloc_xib;
#pragma comment(linker, "/include:_rpmalloc_module_init")
#endif
//end !BUILD_DYNAMIC_LINK
#endif
#else
#include <pthread.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
extern void
rpmalloc_set_main_thread(void);
static pthread_key_t destructor_key;
static void
thread_destructor(void*);
static void __attribute__((constructor))
initializer(void) {
rpmalloc_set_main_thread();
rpmalloc_initialize();
pthread_key_create(&destructor_key, thread_destructor);
}
static void __attribute__((destructor))
finalizer(void) {
rpmalloc_finalize();
}
typedef struct {
void* (*real_start)(void*);
void* real_arg;
} thread_starter_arg;
static void*
thread_starter(void* argptr) {
thread_starter_arg* arg = argptr;
void* (*real_start)(void*) = arg->real_start;
void* real_arg = arg->real_arg;
rpmalloc_thread_initialize();
rpfree(argptr);
pthread_setspecific(destructor_key, (void*)1);
return (*real_start)(real_arg);
}
static void
thread_destructor(void* value) {
(void)sizeof(value);
rpmalloc_thread_finalize(1);
}
#ifdef __APPLE__
static int
pthread_create_proxy(pthread_t* thread,
const pthread_attr_t* attr,
void* (*start_routine)(void*),
void* arg) {
rpmalloc_initialize();
thread_starter_arg* starter_arg = rpmalloc(sizeof(thread_starter_arg));
starter_arg->real_start = start_routine;
starter_arg->real_arg = arg;
return pthread_create(thread, attr, thread_starter, starter_arg);
}
MAC_INTERPOSE_SINGLE(pthread_create_proxy, pthread_create);
#else
#include <dlfcn.h>
int
pthread_create(pthread_t* thread,
const pthread_attr_t* attr,
void* (*start_routine)(void*),
void* arg) {
#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__) || \
defined(__APPLE__) || defined(__HAIKU__)
char fname[] = "pthread_create";
#else
char fname[] = "_pthread_create";
#endif
void* real_pthread_create = dlsym(RTLD_NEXT, fname);
rpmalloc_thread_initialize();
thread_starter_arg* starter_arg = rpmalloc(sizeof(thread_starter_arg));
starter_arg->real_start = start_routine;
starter_arg->real_arg = arg;
return (*(int (*)(pthread_t*, const pthread_attr_t*, void* (*)(void*), void*))real_pthread_create)(thread, attr, thread_starter, starter_arg);
}
#endif
#endif
#endif
#if ENABLE_OVERRIDE
#if defined(__GLIBC__) && defined(__linux__)
void* __libc_malloc(size_t size) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE(1) RPALIAS(rpmalloc)
void* __libc_calloc(size_t count, size_t size) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE2(1, 2) RPALIAS(rpcalloc)
void* __libc_realloc(void* p, size_t size) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE(2) RPALIAS(rprealloc)
void __libc_free(void* p) RPALIAS(rpfree)
void __libc_cfree(void* p) RPALIAS(rpfree)
void* __libc_memalign(size_t align, size_t size) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE(2) RPALIAS(rpmemalign)
int __posix_memalign(void** p, size_t align, size_t size) RPALIAS(rpposix_memalign)
extern void* __libc_valloc(size_t size);
extern void* __libc_pvalloc(size_t size);
void*
__libc_valloc(size_t size) {
return valloc(size);
}
void*
__libc_pvalloc(size_t size) {
return pvalloc(size);
}
#endif
#endif
#if (defined(__GNUC__) || defined(__clang__))
#pragma GCC visibility pop
#endif

3638
source/extern/rpmalloc.c vendored Normal file

File diff suppressed because it is too large Load Diff

373
source/extern/rpmalloc.h vendored Normal file
View File

@ -0,0 +1,373 @@
/* rpmalloc.h - Memory allocator - Public Domain - 2016 Mattias Jansson
*
* This library provides a cross-platform lock free thread caching malloc implementation in C11.
* The latest source code is always available at
*
* https://github.com/mjansson/rpmalloc
*
* This library is put in the public domain; you can redistribute it and/or modify it without any restrictions.
*
*/
#pragma once
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
#if defined(__clang__) || defined(__GNUC__)
# define RPMALLOC_EXPORT __attribute__((visibility("default")))
# define RPMALLOC_ALLOCATOR
# if (defined(__clang_major__) && (__clang_major__ < 4)) || (defined(__GNUC__) && defined(ENABLE_PRELOAD) && ENABLE_PRELOAD)
# define RPMALLOC_ATTRIB_MALLOC
# define RPMALLOC_ATTRIB_ALLOC_SIZE(size)
# define RPMALLOC_ATTRIB_ALLOC_SIZE2(count, size)
# else
# define RPMALLOC_ATTRIB_MALLOC __attribute__((__malloc__))
# define RPMALLOC_ATTRIB_ALLOC_SIZE(size) __attribute__((alloc_size(size)))
# define RPMALLOC_ATTRIB_ALLOC_SIZE2(count, size) __attribute__((alloc_size(count, size)))
# endif
# define RPMALLOC_CDECL
#elif defined(_MSC_VER)
# define RPMALLOC_EXPORT
# define RPMALLOC_ALLOCATOR __declspec(allocator) __declspec(restrict)
# define RPMALLOC_ATTRIB_MALLOC
# define RPMALLOC_ATTRIB_ALLOC_SIZE(size)
# define RPMALLOC_ATTRIB_ALLOC_SIZE2(count,size)
# define RPMALLOC_CDECL __cdecl
#else
# define RPMALLOC_EXPORT
# define RPMALLOC_ALLOCATOR
# define RPMALLOC_ATTRIB_MALLOC
# define RPMALLOC_ATTRIB_ALLOC_SIZE(size)
# define RPMALLOC_ATTRIB_ALLOC_SIZE2(count,size)
# define RPMALLOC_CDECL
#endif
//! Define RPMALLOC_CONFIGURABLE to enable configuring sizes. Will introduce
// a very small overhead due to some size calculations not being compile time constants
#ifndef RPMALLOC_CONFIGURABLE
#define RPMALLOC_CONFIGURABLE 0
#endif
//! Define RPMALLOC_FIRST_CLASS_HEAPS to enable heap based API (rpmalloc_heap_* functions).
// Will introduce a very small overhead to track fully allocated spans in heaps
#ifndef RPMALLOC_FIRST_CLASS_HEAPS
#define RPMALLOC_FIRST_CLASS_HEAPS 0
#endif
//! Flag to rpaligned_realloc to not preserve content in reallocation
#define RPMALLOC_NO_PRESERVE 1
//! Flag to rpaligned_realloc to fail and return null pointer if grow cannot be done in-place,
// in which case the original pointer is still valid (just like a call to realloc which failes to allocate
// a new block).
#define RPMALLOC_GROW_OR_FAIL 2
typedef struct rpmalloc_global_statistics_t {
//! Current amount of virtual memory mapped, all of which might not have been committed (only if ENABLE_STATISTICS=1)
size_t mapped;
//! Peak amount of virtual memory mapped, all of which might not have been committed (only if ENABLE_STATISTICS=1)
size_t mapped_peak;
//! Current amount of memory in global caches for small and medium sizes (<32KiB)
size_t cached;
//! Current amount of memory allocated in huge allocations, i.e larger than LARGE_SIZE_LIMIT which is 2MiB by default (only if ENABLE_STATISTICS=1)
size_t huge_alloc;
//! Peak amount of memory allocated in huge allocations, i.e larger than LARGE_SIZE_LIMIT which is 2MiB by default (only if ENABLE_STATISTICS=1)
size_t huge_alloc_peak;
//! Total amount of memory mapped since initialization (only if ENABLE_STATISTICS=1)
size_t mapped_total;
//! Total amount of memory unmapped since initialization (only if ENABLE_STATISTICS=1)
size_t unmapped_total;
} rpmalloc_global_statistics_t;
typedef struct rpmalloc_thread_statistics_t {
//! Current number of bytes available in thread size class caches for small and medium sizes (<32KiB)
size_t sizecache;
//! Current number of bytes available in thread span caches for small and medium sizes (<32KiB)
size_t spancache;
//! Total number of bytes transitioned from thread cache to global cache (only if ENABLE_STATISTICS=1)
size_t thread_to_global;
//! Total number of bytes transitioned from global cache to thread cache (only if ENABLE_STATISTICS=1)
size_t global_to_thread;
//! Per span count statistics (only if ENABLE_STATISTICS=1)
struct {
//! Currently used number of spans
size_t current;
//! High water mark of spans used
size_t peak;
//! Number of spans transitioned to global cache
size_t to_global;
//! Number of spans transitioned from global cache
size_t from_global;
//! Number of spans transitioned to thread cache
size_t to_cache;
//! Number of spans transitioned from thread cache
size_t from_cache;
//! Number of spans transitioned to reserved state
size_t to_reserved;
//! Number of spans transitioned from reserved state
size_t from_reserved;
//! Number of raw memory map calls (not hitting the reserve spans but resulting in actual OS mmap calls)
size_t map_calls;
} span_use[64];
//! Per size class statistics (only if ENABLE_STATISTICS=1)
struct {
//! Current number of allocations
size_t alloc_current;
//! Peak number of allocations
size_t alloc_peak;
//! Total number of allocations
size_t alloc_total;
//! Total number of frees
size_t free_total;
//! Number of spans transitioned to cache
size_t spans_to_cache;
//! Number of spans transitioned from cache
size_t spans_from_cache;
//! Number of spans transitioned from reserved state
size_t spans_from_reserved;
//! Number of raw memory map calls (not hitting the reserve spans but resulting in actual OS mmap calls)
size_t map_calls;
} size_use[128];
} rpmalloc_thread_statistics_t;
typedef struct rpmalloc_config_t {
//! Map memory pages for the given number of bytes. The returned address MUST be
// aligned to the rpmalloc span size, which will always be a power of two.
// Optionally the function can store an alignment offset in the offset variable
// in case it performs alignment and the returned pointer is offset from the
// actual start of the memory region due to this alignment. The alignment offset
// will be passed to the memory unmap function. The alignment offset MUST NOT be
// larger than 65535 (storable in an uint16_t), if it is you must use natural
// alignment to shift it into 16 bits. If you set a memory_map function, you
// must also set a memory_unmap function or else the default implementation will
// be used for both. This function must be thread safe, it can be called by
// multiple threads simultaneously.
void* (*memory_map)(size_t size, size_t* offset);
//! Unmap the memory pages starting at address and spanning the given number of bytes.
// If release is set to non-zero, the unmap is for an entire span range as returned by
// a previous call to memory_map and that the entire range should be released. The
// release argument holds the size of the entire span range. If release is set to 0,
// the unmap is a partial decommit of a subset of the mapped memory range.
// If you set a memory_unmap function, you must also set a memory_map function or
// else the default implementation will be used for both. This function must be thread
// safe, it can be called by multiple threads simultaneously.
void (*memory_unmap)(void* address, size_t size, size_t offset, size_t release);
//! Called when an assert fails, if asserts are enabled. Will use the standard assert()
// if this is not set.
void (*error_callback)(const char* message);
//! Called when a call to map memory pages fails (out of memory). If this callback is
// not set or returns zero the library will return a null pointer in the allocation
// call. If this callback returns non-zero the map call will be retried. The argument
// passed is the number of bytes that was requested in the map call. Only used if
// the default system memory map function is used (memory_map callback is not set).
int (*map_fail_callback)(size_t size);
//! Size of memory pages. The page size MUST be a power of two. All memory mapping
// requests to memory_map will be made with size set to a multiple of the page size.
// Used if RPMALLOC_CONFIGURABLE is defined to 1, otherwise system page size is used.
size_t page_size;
//! Size of a span of memory blocks. MUST be a power of two, and in [4096,262144]
// range (unless 0 - set to 0 to use the default span size). Used if RPMALLOC_CONFIGURABLE
// is defined to 1.
size_t span_size;
//! Number of spans to map at each request to map new virtual memory blocks. This can
// be used to minimize the system call overhead at the cost of virtual memory address
// space. The extra mapped pages will not be written until actually used, so physical
// committed memory should not be affected in the default implementation. Will be
// aligned to a multiple of spans that match memory page size in case of huge pages.
size_t span_map_count;
//! Enable use of large/huge pages. If this flag is set to non-zero and page size is
// zero, the allocator will try to enable huge pages and auto detect the configuration.
// If this is set to non-zero and page_size is also non-zero, the allocator will
// assume huge pages have been configured and enabled prior to initializing the
// allocator.
// For Windows, see https://docs.microsoft.com/en-us/windows/desktop/memory/large-page-support
// For Linux, see https://www.kernel.org/doc/Documentation/vm/hugetlbpage.txt
int enable_huge_pages;
//! Respectively allocated pages and huge allocated pages names for systems
// supporting it to be able to distinguish among anonymous regions.
const char *page_name;
const char *huge_page_name;
} rpmalloc_config_t;
//! Initialize allocator with default configuration
RPMALLOC_EXPORT int
rpmalloc_initialize(void);
//! Initialize allocator with given configuration
RPMALLOC_EXPORT int
rpmalloc_initialize_config(const rpmalloc_config_t* config);
//! Get allocator configuration
RPMALLOC_EXPORT const rpmalloc_config_t*
rpmalloc_config(void);
//! Finalize allocator
RPMALLOC_EXPORT void
rpmalloc_finalize(void);
//! Initialize allocator for calling thread
RPMALLOC_EXPORT void
rpmalloc_thread_initialize(void);
//! Finalize allocator for calling thread
RPMALLOC_EXPORT void
rpmalloc_thread_finalize(int release_caches);
//! Perform deferred deallocations pending for the calling thread heap
RPMALLOC_EXPORT void
rpmalloc_thread_collect(void);
//! Query if allocator is initialized for calling thread
RPMALLOC_EXPORT int
rpmalloc_is_thread_initialized(void);
//! Get per-thread statistics
RPMALLOC_EXPORT void
rpmalloc_thread_statistics(rpmalloc_thread_statistics_t* stats);
//! Get global statistics
RPMALLOC_EXPORT void
rpmalloc_global_statistics(rpmalloc_global_statistics_t* stats);
//! Dump all statistics in human readable format to file (should be a FILE*)
RPMALLOC_EXPORT void
rpmalloc_dump_statistics(void* file);
//! Allocate a memory block of at least the given size
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void*
rpmalloc(size_t size) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE(1);
//! Free the given memory block
RPMALLOC_EXPORT void
rpfree(void* ptr);
//! Allocate a memory block of at least the given size and zero initialize it
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void*
rpcalloc(size_t num, size_t size) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE2(1, 2);
//! Reallocate the given block to at least the given size
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void*
rprealloc(void* ptr, size_t size) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE(2);
//! Reallocate the given block to at least the given size and alignment,
// with optional control flags (see RPMALLOC_NO_PRESERVE).
// Alignment must be a power of two and a multiple of sizeof(void*),
// and should ideally be less than memory page size. A caveat of rpmalloc
// internals is that this must also be strictly less than the span size (default 64KiB)
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void*
rpaligned_realloc(void* ptr, size_t alignment, size_t size, size_t oldsize, unsigned int flags) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE(3);
//! Allocate a memory block of at least the given size and alignment.
// Alignment must be a power of two and a multiple of sizeof(void*),
// and should ideally be less than memory page size. A caveat of rpmalloc
// internals is that this must also be strictly less than the span size (default 64KiB)
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void*
rpaligned_alloc(size_t alignment, size_t size) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE(2);
//! Allocate a memory block of at least the given size and alignment, and zero initialize it.
// Alignment must be a power of two and a multiple of sizeof(void*),
// and should ideally be less than memory page size. A caveat of rpmalloc
// internals is that this must also be strictly less than the span size (default 64KiB)
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void*
rpaligned_calloc(size_t alignment, size_t num, size_t size) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE2(2, 3);
//! Allocate a memory block of at least the given size and alignment.
// Alignment must be a power of two and a multiple of sizeof(void*),
// and should ideally be less than memory page size. A caveat of rpmalloc
// internals is that this must also be strictly less than the span size (default 64KiB)
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void*
rpmemalign(size_t alignment, size_t size) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE(2);
//! Allocate a memory block of at least the given size and alignment.
// Alignment must be a power of two and a multiple of sizeof(void*),
// and should ideally be less than memory page size. A caveat of rpmalloc
// internals is that this must also be strictly less than the span size (default 64KiB)
RPMALLOC_EXPORT int
rpposix_memalign(void** memptr, size_t alignment, size_t size);
//! Query the usable size of the given memory block (from given pointer to the end of block)
RPMALLOC_EXPORT size_t
rpmalloc_usable_size(void* ptr);
//! Dummy empty function for forcing linker symbol inclusion
RPMALLOC_EXPORT void
rpmalloc_linker_reference(void);
#if RPMALLOC_FIRST_CLASS_HEAPS
//! Heap type
typedef struct heap_t rpmalloc_heap_t;
//! Acquire a new heap. Will reuse existing released heaps or allocate memory for a new heap
// if none available. Heap API is implemented with the strict assumption that only one single
// thread will call heap functions for a given heap at any given time, no functions are thread safe.
RPMALLOC_EXPORT rpmalloc_heap_t*
rpmalloc_heap_acquire(void);
//! Release a heap (does NOT free the memory allocated by the heap, use rpmalloc_heap_free_all before destroying the heap).
// Releasing a heap will enable it to be reused by other threads. Safe to pass a null pointer.
RPMALLOC_EXPORT void
rpmalloc_heap_release(rpmalloc_heap_t* heap);
//! Allocate a memory block of at least the given size using the given heap.
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void*
rpmalloc_heap_alloc(rpmalloc_heap_t* heap, size_t size) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE(2);
//! Allocate a memory block of at least the given size using the given heap. The returned
// block will have the requested alignment. Alignment must be a power of two and a multiple of sizeof(void*),
// and should ideally be less than memory page size. A caveat of rpmalloc
// internals is that this must also be strictly less than the span size (default 64KiB).
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void*
rpmalloc_heap_aligned_alloc(rpmalloc_heap_t* heap, size_t alignment, size_t size) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE(3);
//! Allocate a memory block of at least the given size using the given heap and zero initialize it.
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void*
rpmalloc_heap_calloc(rpmalloc_heap_t* heap, size_t num, size_t size) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE2(2, 3);
//! Allocate a memory block of at least the given size using the given heap and zero initialize it. The returned
// block will have the requested alignment. Alignment must either be zero, or a power of two and a multiple of sizeof(void*),
// and should ideally be less than memory page size. A caveat of rpmalloc
// internals is that this must also be strictly less than the span size (default 64KiB).
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void*
rpmalloc_heap_aligned_calloc(rpmalloc_heap_t* heap, size_t alignment, size_t num, size_t size) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE2(2, 3);
//! Reallocate the given block to at least the given size. The memory block MUST be allocated
// by the same heap given to this function.
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void*
rpmalloc_heap_realloc(rpmalloc_heap_t* heap, void* ptr, size_t size, unsigned int flags) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE(3);
//! Reallocate the given block to at least the given size. The memory block MUST be allocated
// by the same heap given to this function. The returned block will have the requested alignment.
// Alignment must be either zero, or a power of two and a multiple of sizeof(void*), and should ideally be
// less than memory page size. A caveat of rpmalloc internals is that this must also be strictly less than
// the span size (default 64KiB).
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void*
rpmalloc_heap_aligned_realloc(rpmalloc_heap_t* heap, void* ptr, size_t alignment, size_t size, unsigned int flags) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE(4);
//! Free the given memory block from the given heap. The memory block MUST be allocated
// by the same heap given to this function.
RPMALLOC_EXPORT void
rpmalloc_heap_free(rpmalloc_heap_t* heap, void* ptr);
//! Free all memory allocated by the heap
RPMALLOC_EXPORT void
rpmalloc_heap_free_all(rpmalloc_heap_t* heap);
//! Set the given heap as the current heap for the calling thread. A heap MUST only be current heap
// for a single thread, a heap can never be shared between multiple threads. The previous
// current heap for the calling thread is released to be reused by other threads.
RPMALLOC_EXPORT void
rpmalloc_heap_thread_set_current(rpmalloc_heap_t* heap);
//! Returns which heap the given pointer is allocated on
RPMALLOC_EXPORT rpmalloc_heap_t*
rpmalloc_get_heap_for_ptr(void* ptr);
#endif
#ifdef __cplusplus
}
#endif

111
source/extern/rpnew.h vendored Normal file
View File

@ -0,0 +1,111 @@
#ifdef __cplusplus
#include <new>
#include <rpmalloc.h>
#ifndef __CRTDECL
#define __CRTDECL
#endif
extern void __CRTDECL
operator delete(void* p) noexcept {
rpfree(p);
}
extern void __CRTDECL
operator delete[](void* p) noexcept {
rpfree(p);
}
extern void* __CRTDECL
operator new(std::size_t size) noexcept(false) {
return rpmalloc(size);
}
extern void* __CRTDECL
operator new[](std::size_t size) noexcept(false) {
return rpmalloc(size);
}
extern void* __CRTDECL
operator new(std::size_t size, const std::nothrow_t& tag) noexcept {
(void)sizeof(tag);
return rpmalloc(size);
}
extern void* __CRTDECL
operator new[](std::size_t size, const std::nothrow_t& tag) noexcept {
(void)sizeof(tag);
return rpmalloc(size);
}
#if (__cplusplus >= 201402L || _MSC_VER >= 1916)
extern void __CRTDECL
operator delete(void* p, std::size_t size) noexcept {
(void)sizeof(size);
rpfree(p);
}
extern void __CRTDECL
operator delete[](void* p, std::size_t size) noexcept {
(void)sizeof(size);
rpfree(p);
}
#endif
#if (__cplusplus > 201402L || defined(__cpp_aligned_new))
extern void __CRTDECL
operator delete(void* p, std::align_val_t align) noexcept {
(void)sizeof(align);
rpfree(p);
}
extern void __CRTDECL
operator delete[](void* p, std::align_val_t align) noexcept {
(void)sizeof(align);
rpfree(p);
}
extern void __CRTDECL
operator delete(void* p, std::size_t size, std::align_val_t align) noexcept {
(void)sizeof(size);
(void)sizeof(align);
rpfree(p);
}
extern void __CRTDECL
operator delete[](void* p, std::size_t size, std::align_val_t align) noexcept {
(void)sizeof(size);
(void)sizeof(align);
rpfree(p);
}
extern void* __CRTDECL
operator new(std::size_t size, std::align_val_t align) noexcept(false) {
return rpaligned_alloc(static_cast<size_t>(align), size);
}
extern void* __CRTDECL
operator new[](std::size_t size, std::align_val_t align) noexcept(false) {
return rpaligned_alloc(static_cast<size_t>(align), size);
}
extern void* __CRTDECL
operator new(std::size_t size, std::align_val_t align, const std::nothrow_t& tag) noexcept {
(void)sizeof(tag);
return rpaligned_alloc(static_cast<size_t>(align), size);
}
extern void* __CRTDECL
operator new[](std::size_t size, std::align_val_t align, const std::nothrow_t& tag) noexcept {
(void)sizeof(tag);
return rpaligned_alloc(static_cast<size_t>(align), size);
}
#endif
#endif

View File

@ -81,7 +81,7 @@ void BaseScriptPane::tick(float dt) {
} }
for (auto p : m_canvasKeyCallbacks) { for (auto p : m_canvasKeyCallbacks) {
for (auto const& keyEvent : p.first->pullKeyEvents()) for (auto const& keyEvent : p.first->pullKeyEvents())
m_script.invoke(p.second, (int)keyEvent.key, keyEvent.keyDown); m_script.invoke(p.second, (int)keyEvent.key, keyEvent.keyDown, KeyNames.getRight(keyEvent.key));
} }
m_script.update(m_script.updateDt(dt)); m_script.update(m_script.updateDt(dt));

View File

@ -77,14 +77,15 @@ void CharSelectionPane::updateCharacterPlates() {
auto updatePlayerLine = [this](String name, unsigned scrollPosition) { auto updatePlayerLine = [this](String name, unsigned scrollPosition) {
auto charSelector = fetchChild<LargeCharPlateWidget>(name); auto charSelector = fetchChild<LargeCharPlateWidget>(name);
if (auto playerUuid = m_playerStorage->playerUuidAt(scrollPosition)) { if (auto playerUuid = m_playerStorage->playerUuidAt(scrollPosition)) {
auto player = m_playerStorage->loadPlayer(*playerUuid); if (auto player = m_playerStorage->loadPlayer(*playerUuid)) {
player->humanoid()->setFacingDirection(Direction::Right); player->humanoid()->setFacingDirection(Direction::Right);
charSelector->setPlayer(player); charSelector->setPlayer(player);
charSelector->enableDelete([this, playerUuid](Widget*) { m_deleteCallback(*playerUuid); }); charSelector->enableDelete([this, playerUuid](Widget*) { m_deleteCallback(*playerUuid); });
} else { return;
charSelector->setPlayer(PlayerPtr()); }
charSelector->disableDelete();
} }
charSelector->setPlayer(PlayerPtr());
charSelector->disableDelete();
}; };
updatePlayerLine("charSelector1", m_downScroll + 0); updatePlayerLine("charSelector1", m_downScroll + 0);

View File

@ -15,9 +15,9 @@
namespace Star { namespace Star {
ClientCommandProcessor::ClientCommandProcessor(UniverseClientPtr universeClient, CinematicPtr cinematicOverlay, ClientCommandProcessor::ClientCommandProcessor(UniverseClientPtr universeClient, CinematicPtr cinematicOverlay,
MainInterfacePaneManager* paneManager, StringMap<StringList> macroCommands) MainInterfacePaneManager* paneManager, StringMap<StringList> macroCommands)
: m_universeClient(std::move(universeClient)), m_cinematicOverlay(std::move(cinematicOverlay)), : m_universeClient(std::move(universeClient)), m_cinematicOverlay(std::move(cinematicOverlay)),
m_paneManager(paneManager), m_macroCommands(std::move(macroCommands)) { m_paneManager(paneManager), m_macroCommands(std::move(macroCommands)) {
m_builtinCommands = { m_builtinCommands = {
{"reload", bind(&ClientCommandProcessor::reload, this)}, {"reload", bind(&ClientCommandProcessor::reload, this)},
{"whoami", bind(&ClientCommandProcessor::whoami, this)}, {"whoami", bind(&ClientCommandProcessor::whoami, this)},
@ -51,7 +51,8 @@ ClientCommandProcessor::ClientCommandProcessor(UniverseClientPtr universeClient,
{"maketechavailable", bind(&ClientCommandProcessor::makeTechAvailable, this, _1)}, {"maketechavailable", bind(&ClientCommandProcessor::makeTechAvailable, this, _1)},
{"enabletech", bind(&ClientCommandProcessor::enableTech, this, _1)}, {"enabletech", bind(&ClientCommandProcessor::enableTech, this, _1)},
{"upgradeship", bind(&ClientCommandProcessor::upgradeShip, this, _1)}, {"upgradeship", bind(&ClientCommandProcessor::upgradeShip, this, _1)},
{"swap", bind(&ClientCommandProcessor::swap, this, _1)} {"swap", bind(&ClientCommandProcessor::swap, this, _1)},
{"respawnInWorld", bind(&ClientCommandProcessor::respawnInWorld, this, _1)}
}; };
} }
@ -91,9 +92,12 @@ StringList ClientCommandProcessor::handleCommand(String const& commandLine) {
} }
} else { } else {
auto player = m_universeClient->mainPlayer(); auto player = m_universeClient->mainPlayer();
if (auto messageResult = player->receiveMessage(connectionForEntity(player->entityId()), "/" + command, { allArguments })) if (auto messageResult = player->receiveMessage(connectionForEntity(player->entityId()), "/" + command, {allArguments})) {
result.append(messageResult->isType(Json::Type::String) ? *messageResult->stringPtr() : messageResult->repr(1, true)); if (messageResult->isType(Json::Type::String))
else result.append(*messageResult->stringPtr());
else if (!messageResult->isNull())
result.append(messageResult->repr(1, true));
} else
m_universeClient->sendChat(commandLine, ChatSendMode::Broadcast); m_universeClient->sendChat(commandLine, ChatSendMode::Broadcast);
} }
return result; return result;
@ -125,7 +129,7 @@ String ClientCommandProcessor::reload() {
String ClientCommandProcessor::whoami() { String ClientCommandProcessor::whoami() {
return strf("Client: You are {}. You are {}an Admin.", return strf("Client: You are {}. You are {}an Admin.",
m_universeClient->mainPlayer()->name(), m_universeClient->mainPlayer()->isAdmin() ? "" : "not "); m_universeClient->mainPlayer()->name(), m_universeClient->mainPlayer()->isAdmin() ? "" : "not ");
} }
String ClientCommandProcessor::gravity() { String ClientCommandProcessor::gravity() {
@ -183,7 +187,7 @@ String ClientCommandProcessor::setGravity(String const& argumentsString) {
return "You must be an admin to use this command."; return "You must be an admin to use this command.";
m_universeClient->worldClient()->overrideGravity(lexicalCast<float>(arguments.at(0))); m_universeClient->worldClient()->overrideGravity(lexicalCast<float>(arguments.at(0)));
return strf("Gravity set to {}, the change is LOCAL ONLY", arguments.at(0)); return strf("Gravity set to {} (This is client-side!)", arguments.at(0));
} }
String ClientCommandProcessor::resetGravity() { String ClientCommandProcessor::resetGravity() {
@ -270,8 +274,8 @@ String ClientCommandProcessor::previewNewQuest(String const& argumentsString) {
return "You must be an admin to use this command."; return "You must be an admin to use this command.";
return previewQuestPane(arguments, [this](QuestPtr const& quest) { return previewQuestPane(arguments, [this](QuestPtr const& quest) {
return make_shared<NewQuestInterface>(m_universeClient->questManager(), quest, m_universeClient->mainPlayer()); return make_shared<NewQuestInterface>(m_universeClient->questManager(), quest, m_universeClient->mainPlayer());
}); });
} }
String ClientCommandProcessor::previewQuestComplete(String const& argumentsString) { String ClientCommandProcessor::previewQuestComplete(String const& argumentsString) {
@ -280,8 +284,8 @@ String ClientCommandProcessor::previewQuestComplete(String const& argumentsStrin
return "You must be an admin to use this command."; return "You must be an admin to use this command.";
return previewQuestPane(arguments, [this](QuestPtr const& quest) { return previewQuestPane(arguments, [this](QuestPtr const& quest) {
return make_shared<QuestCompleteInterface>(quest, m_universeClient->mainPlayer(), CinematicPtr{}); return make_shared<QuestCompleteInterface>(quest, m_universeClient->mainPlayer(), CinematicPtr{});
}); });
} }
String ClientCommandProcessor::previewQuestFailed(String const& argumentsString) { String ClientCommandProcessor::previewQuestFailed(String const& argumentsString) {
@ -290,8 +294,8 @@ String ClientCommandProcessor::previewQuestFailed(String const& argumentsString)
return "You must be an admin to use this command."; return "You must be an admin to use this command.";
return previewQuestPane(arguments, [this](QuestPtr const& quest) { return previewQuestPane(arguments, [this](QuestPtr const& quest) {
return make_shared<QuestFailedInterface>(quest, m_universeClient->mainPlayer()); return make_shared<QuestFailedInterface>(quest, m_universeClient->mainPlayer());
}); });
} }
String ClientCommandProcessor::clearScannedObjects() { String ClientCommandProcessor::clearScannedObjects() {
@ -424,4 +428,16 @@ String ClientCommandProcessor::swap(String const& argumentsString) {
return "Failed to swap player"; return "Failed to swap player";
} }
String ClientCommandProcessor::respawnInWorld(String const& argumentsString) {
auto arguments = m_parser.tokenizeToStringList(argumentsString);
auto worldClient = m_universeClient->worldClient();
if (arguments.size() == 0)
return strf("Respawn in this world is currently {}", worldClient->respawnInWorld() ? "true" : "false");
bool respawnInWorld = Json::parse(arguments.at(0)).toBool();
worldClient->setRespawnInWorld(respawnInWorld);
return strf("Respawn in this world set to {} (This is client-side!)", respawnInWorld ? "true" : "false");
}
} }

View File

@ -58,6 +58,7 @@ private:
String enableTech(String const& argumentsString); String enableTech(String const& argumentsString);
String upgradeShip(String const& argumentsString); String upgradeShip(String const& argumentsString);
String swap(String const& argumentsString); String swap(String const& argumentsString);
String respawnInWorld(String const& argumentsString);
UniverseClientPtr m_universeClient; UniverseClientPtr m_universeClient;
CinematicPtr m_cinematicOverlay; CinematicPtr m_cinematicOverlay;

View File

@ -97,10 +97,10 @@ GraphicsMenu::GraphicsMenu() {
Root::singleton().configuration()->set("monochromeLighting", checked); Root::singleton().configuration()->set("monochromeLighting", checked);
syncGui(); syncGui();
}); });
reader.registerCallback("objectLightingCheckbox", [=](Widget*) { reader.registerCallback("newLightingCheckbox", [=](Widget*) {
bool checked = fetchChild<ButtonWidget>("objectLightingCheckbox")->isChecked(); bool checked = fetchChild<ButtonWidget>("newLightingCheckbox")->isChecked();
m_localChanges.set("newObjectLighting", checked); m_localChanges.set("newLighting", checked);
Root::singleton().configuration()->set("newObjectLighting", checked); Root::singleton().configuration()->set("newLighting", checked);
syncGui(); syncGui();
}); });
@ -162,7 +162,7 @@ StringList const GraphicsMenu::ConfigKeys = {
"antiAliasing", "antiAliasing",
"hardwareCursor", "hardwareCursor",
"monochromeLighting", "monochromeLighting",
"newObjectLighting" "newLighting"
}; };
void GraphicsMenu::initConfig() { void GraphicsMenu::initConfig() {
@ -229,7 +229,7 @@ void GraphicsMenu::syncGui() {
fetchChild<ButtonWidget>("multiTextureCheckbox")->setChecked(m_localChanges.get("useMultiTexturing").optBool().value(true)); fetchChild<ButtonWidget>("multiTextureCheckbox")->setChecked(m_localChanges.get("useMultiTexturing").optBool().value(true));
fetchChild<ButtonWidget>("antiAliasingCheckbox")->setChecked(m_localChanges.get("antiAliasing").toBool()); fetchChild<ButtonWidget>("antiAliasingCheckbox")->setChecked(m_localChanges.get("antiAliasing").toBool());
fetchChild<ButtonWidget>("monochromeCheckbox")->setChecked(m_localChanges.get("monochromeLighting").toBool()); fetchChild<ButtonWidget>("monochromeCheckbox")->setChecked(m_localChanges.get("monochromeLighting").toBool());
fetchChild<ButtonWidget>("objectLightingCheckbox")->setChecked(m_localChanges.get("newObjectLighting").optBool().value(true)); fetchChild<ButtonWidget>("newLightingCheckbox")->setChecked(m_localChanges.get("newLighting").optBool().value(true));
fetchChild<ButtonWidget>("hardwareCursorCheckbox")->setChecked(m_localChanges.get("hardwareCursor").toBool()); fetchChild<ButtonWidget>("hardwareCursorCheckbox")->setChecked(m_localChanges.get("hardwareCursor").toBool());
} }

View File

@ -238,7 +238,8 @@ PanePtr InventoryPane::createTooltip(Vec2I const& screenPosition) {
if (auto techIcon = fetchChild<ImageWidget>(strf("tech{}", p.second))) { if (auto techIcon = fetchChild<ImageWidget>(strf("tech{}", p.second))) {
if (techIcon->screenBoundRect().contains(screenPosition)) { if (techIcon->screenBoundRect().contains(screenPosition)) {
if (auto techModule = m_player->techs()->equippedTechs().maybe(p.first)) if (auto techModule = m_player->techs()->equippedTechs().maybe(p.first))
return SimpleTooltipBuilder::buildTooltip(techDatabase->tech(*techModule).description); if (techDatabase->contains(*techModule))
return SimpleTooltipBuilder::buildTooltip(techDatabase->tech(*techModule).description);
} }
} }
} }
@ -325,10 +326,13 @@ void InventoryPane::update(float dt) {
auto techDatabase = Root::singleton().techDatabase(); auto techDatabase = Root::singleton().techDatabase();
for (auto const& p : TechTypeNames) { for (auto const& p : TechTypeNames) {
if (auto techIcon = fetchChild<ImageWidget>(strf("tech{}", p.second))) { if (auto techIcon = fetchChild<ImageWidget>(strf("tech{}", p.second))) {
if (auto techModule = m_player->techs()->equippedTechs().maybe(p.first)) if (auto techModule = m_player->techs()->equippedTechs().maybe(p.first)) {
techIcon->setImage(techDatabase->tech(*techModule).icon); if (techDatabase->contains(*techModule)) {
else techIcon->setImage(techDatabase->tech(*techModule).icon);
techIcon->setImage(""); continue;
}
}
techIcon->setImage("");
} }
} }

View File

@ -760,23 +760,30 @@ void MainInterface::update(float dt) {
m_chatBubbleManager->setCamera(m_worldPainter->camera()); m_chatBubbleManager->setCamera(m_worldPainter->camera());
if (auto worldClient = m_client->worldClient()) { if (auto worldClient = m_client->worldClient()) {
auto chatActions = worldClient->pullPendingChatActions(); auto chatActions = worldClient->pullPendingChatActions();
auto portraitActions = chatActions.filtered([](ChatAction action) { return action.is<PortraitChatAction>(); });
for (auto action : portraitActions) { for (auto& action : chatActions) {
PortraitChatAction portraitAction = action.get<PortraitChatAction>(); if (action.is<PortraitChatAction>()) {
PortraitChatAction& portraitAction = action.get<PortraitChatAction>();
String name; String name;
if (auto npc = as<Npc>(worldClient->entity(portraitAction.entity))) if (auto npc = as<Npc>(worldClient->entity(portraitAction.entity)))
name = npc->name(); name = npc->name();
ChatReceivedMessage message = { ChatReceivedMessage message = {
{ MessageContext::World }, {MessageContext::World},
ServerConnectionId, ServerConnectionId,
Text::stripEscapeCodes(name), Text::stripEscapeCodes(name),
Text::stripEscapeCodes(portraitAction.text), Text::stripEscapeCodes(portraitAction.text),
Text::stripEscapeCodes(portraitAction.portrait.replace("<frame>", "0")) Text::stripEscapeCodes(portraitAction.portrait.replace("<frame>", "0"))};
}; m_chat->addMessages({message}, false);
m_chat->addMessages({message}, false); } else if (action.is<SayChatAction>()) {
SayChatAction& sayAction = action.get<SayChatAction>();
if (sayAction.config) {
if (auto message = sayAction.config.opt("message"))
m_chat->addMessages({ChatReceivedMessage(*message)}, sayAction.config.getBool("showPane", false));
}
}
} }
m_chatBubbleManager->addChatActions(chatActions); m_chatBubbleManager->addChatActions(chatActions);

View File

@ -211,14 +211,26 @@ void Voice::loadJson(Json const& config, bool skipSave) {
m_lastInputTime = 0; m_lastInputTime = 0;
} }
bool shouldResetEncoder = false;
if (auto channelMode = config.optString("channelMode")) { if (auto channelMode = config.optString("channelMode")) {
if (change(m_channelMode, VoiceChannelModeNames.getLeft(*channelMode), changed)) { if (change(m_channelMode, VoiceChannelModeNames.getLeft(*channelMode), changed)) {
closeDevice(); closeDevice();
resetEncoder(); shouldResetEncoder = true;
resetDevice(); resetDevice();
} }
} }
// not saving this setting to disk, as it's just for audiophiles
// don't want someone fudging their bitrate from the intended defaults and forgetting
if (auto bitrate = config.opt("bitrate")) {
unsigned newBitrate = bitrate->canConvert(Json::Type::Int)
? clamp((unsigned)bitrate->toUInt(), 6000u, 510000u) : 0;
shouldResetEncoder |= change(m_bitrate, newBitrate, changed);
}
if (shouldResetEncoder)
resetEncoder();
if (changed && !skipSave) if (changed && !skipSave)
scheduleSave(); scheduleSave();
} }
@ -607,7 +619,8 @@ void Voice::resetEncoder() {
int channels = encoderChannels(); int channels = encoderChannels();
MutexLocker locker(m_threadMutex); MutexLocker locker(m_threadMutex);
m_encoder.reset(createEncoder(channels)); m_encoder.reset(createEncoder(channels));
opus_encoder_ctl(m_encoder.get(), OPUS_SET_BITRATE(channels == 2 ? 50000 : 24000)); int bitrate = m_bitrate > 0 ? (int)m_bitrate : (channels == 2 ? 50000 : 24000);
opus_encoder_ctl(m_encoder.get(), OPUS_SET_BITRATE(bitrate));
} }
void Voice::resetDevice() { void Voice::resetDevice() {

View File

@ -195,6 +195,7 @@ private:
Maybe<String> m_deviceName; Maybe<String> m_deviceName;
VoiceInputMode m_inputMode; VoiceInputMode m_inputMode;
VoiceChannelMode m_channelMode; VoiceChannelMode m_channelMode;
unsigned m_bitrate = 0;
ThreadFunction<void> m_thread; ThreadFunction<void> m_thread;
Mutex m_threadMutex; Mutex m_threadMutex;

View File

@ -55,7 +55,7 @@ ChatReceivedMessage::ChatReceivedMessage(Json const& json) : ChatReceivedMessage
fromConnection = json.getUInt("fromConnection", 0); fromConnection = json.getUInt("fromConnection", 0);
fromNick = json.getString("fromNick", ""); fromNick = json.getString("fromNick", "");
portrait = json.getString("portrait", ""); portrait = json.getString("portrait", "");
text = json.getString("text"); text = json.getString("text", "");
} }
Json ChatReceivedMessage::toJson() const { Json ChatReceivedMessage::toJson() const {

View File

@ -53,12 +53,14 @@ String CommandProcessor::help(ConnectionId connectionId, String const& argumentS
auto assets = Root::singleton().assets(); auto assets = Root::singleton().assets();
auto basicCommands = assets->json("/help.config:basicCommands"); auto basicCommands = assets->json("/help.config:basicCommands");
auto openSbCommands = assets->json("/help.config:openSbCommands");
auto adminCommands = assets->json("/help.config:adminCommands"); auto adminCommands = assets->json("/help.config:adminCommands");
auto debugCommands = assets->json("/help.config:debugCommands"); auto debugCommands = assets->json("/help.config:debugCommands");
auto openSbDebugCommands = assets->json("/help.config:openSbDebugCommands");
if (arguments.size()) { if (arguments.size()) {
if (arguments.size() >= 1) { if (arguments.size() >= 1) {
if (auto helpText = basicCommands.optString(arguments[0]).orMaybe(adminCommands.optString(arguments[0])).orMaybe(debugCommands.optString(arguments[0]))) if (auto helpText = basicCommands.optString(arguments[0]).orMaybe(openSbCommands.optString(arguments[0])).orMaybe(adminCommands.optString(arguments[0])).orMaybe(debugCommands.optString(arguments[0])).orMaybe(openSbDebugCommands.optString(arguments[0])))
return *helpText; return *helpText;
} }
} }
@ -74,12 +76,18 @@ String CommandProcessor::help(ConnectionId connectionId, String const& argumentS
String basicHelpFormat = assets->json("/help.config:basicHelpText").toString(); String basicHelpFormat = assets->json("/help.config:basicHelpText").toString();
res = res + strf(basicHelpFormat.utf8Ptr(), commandDescriptions(basicCommands)); res = res + strf(basicHelpFormat.utf8Ptr(), commandDescriptions(basicCommands));
String openSbHelpFormat = assets->json("/help.config:openSbHelpText").toString();
res = res + "\n" + strf(openSbHelpFormat.utf8Ptr(), commandDescriptions(openSbCommands));
if (!adminCheck(connectionId, "")) { if (!adminCheck(connectionId, "")) {
String adminHelpFormat = assets->json("/help.config:adminHelpText").toString(); String adminHelpFormat = assets->json("/help.config:adminHelpText").toString();
res = res + "\n" + strf(adminHelpFormat.utf8Ptr(), commandDescriptions(adminCommands)); res = res + "\n" + strf(adminHelpFormat.utf8Ptr(), commandDescriptions(adminCommands));
String debugHelpFormat = assets->json("/help.config:debugHelpText").toString(); String debugHelpFormat = assets->json("/help.config:debugHelpText").toString();
res = res + "\n" + strf(debugHelpFormat.utf8Ptr(), commandDescriptions(debugCommands)); res = res + "\n" + strf(debugHelpFormat.utf8Ptr(), commandDescriptions(debugCommands));
String openSbDebugHelpFormat = assets->json("/help.config:openSbDebugHelpText").toString();
res = res + "\n" + strf(openSbDebugHelpFormat.utf8Ptr(), commandDescriptions(openSbDebugCommands));
} }
res = res + "\n" + basicCommands.getString("help"); res = res + "\n" + basicCommands.getString("help");
@ -209,7 +217,7 @@ String CommandProcessor::timewarp(ConnectionId connectionId, String const& argum
return "Great Scott! We can't go back in time!"; return "Great Scott! We can't go back in time!";
m_universe->universeClock()->adjustTime(time); m_universe->universeClock()->adjustTime(time);
return strf("It's just a jump to the {}...", time > 0.0 ? "left" : "right"); return time > 0.0 ? "It's just a jump to the left..." : "And then a step to the right...";
} catch (BadLexicalCast const&) { } catch (BadLexicalCast const&) {
return strf("Could not parse the argument {} as a time adjustment", arguments[0]); return strf("Could not parse the argument {} as a time adjustment", arguments[0]);
} }

View File

@ -654,12 +654,10 @@ namespace Dungeon {
}); });
ground[1] = max(ground[1], liquid[1]); ground[1] = max(ground[1], liquid[1]);
if (air.y() < ground.y()) if (air.y() < ground.y())
throw DungeonException("Invalid ground vs air contraint. Ground at: " + toString(ground.y()) + " Air at: " throw DungeonException::format(
+ toString(air.y()) "Invalid ground vs air contraint! Ground {} can't be above air {}"
+ " Pixels: highest ground:" " (try moving your 'require there be air here' anchors above any other 'require there be (something) here' anchors.)",
+ toString(ground) ground, air);
+ " lowest air:"
+ toString(air));
return air.y(); return air.y();
} }

View File

@ -122,6 +122,22 @@ Json Input::inputEventToJson(InputEvent const& input) {
{"mouseMove", jsonFromVec2I(mouseMove->mouseMove)}, {"mouseMove", jsonFromVec2I(mouseMove->mouseMove)},
{"mousePosition", jsonFromVec2I(mouseMove->mousePosition)} {"mousePosition", jsonFromVec2I(mouseMove->mousePosition)}
}; };
} else if (auto controllerDown = input.ptr<ControllerButtonDownEvent>()) {
type = "ControllerButtonDown";
data = JsonObject{
{"controllerButton", ControllerButtonNames.getRight(controllerDown->controllerButton)},
{"controller", controllerDown->controller}};
} else if (auto controllerUp = input.ptr<ControllerButtonUpEvent>()) {
type = "ControllerButtonUp";
data = JsonObject{
{"controllerButton", ControllerButtonNames.getRight(controllerUp->controllerButton)},
{"controller", controllerUp->controller}};
} else if (auto controllerAxis = input.ptr<ControllerAxisEvent>()) {
type = "ControllerAxis";
data = JsonObject{
{"controllerAxis", ControllerAxisNames.getRight(controllerAxis->controllerAxis)},
{"controllerAxisValue", controllerAxis->controllerAxisValue},
{"controller", controllerAxis->controller}};
} }
if (data) { if (data) {
@ -144,19 +160,28 @@ Input::Bind Input::bindFromJson(Json const& json) {
if (type == "key") { if (type == "key") {
KeyBind keyBind; KeyBind keyBind;
keyBind.key = KeyNames.getLeft(value.toString()); if (auto key = KeyNames.maybeLeft(value.toString()))
keyBind.key = *key;
else
return bind;
keyBind.mods = keyModsFromJson(json.getArray("mods", {}), &keyBind.priority); keyBind.mods = keyModsFromJson(json.getArray("mods", {}), &keyBind.priority);
bind = std::move(keyBind); bind = std::move(keyBind);
} }
else if (type == "mouse") { else if (type == "mouse") {
MouseBind mouseBind; MouseBind mouseBind;
mouseBind.button = MouseButtonNames.getLeft(value.toString()); if (auto button = MouseButtonNames.maybeLeft(value.toString()))
mouseBind.button = *button;
else
return bind;
mouseBind.mods = keyModsFromJson(json.getArray("mods", {}), &mouseBind.priority); mouseBind.mods = keyModsFromJson(json.getArray("mods", {}), &mouseBind.priority);
bind = std::move(mouseBind); bind = std::move(mouseBind);
} }
else if (type == "controller") { else if (type == "controller") {
ControllerBind controllerBind; ControllerBind controllerBind;
controllerBind.button = ControllerButtonNames.getLeft(value.toString()); if (auto button = ControllerButtonNames.maybeLeft(value.toString()))
controllerBind.button = *button;
else
return bind;
controllerBind.controller = json.getUInt("controller", 0); controllerBind.controller = json.getUInt("controller", 0);
bind = std::move(controllerBind); bind = std::move(controllerBind);
} }
@ -186,7 +211,8 @@ Json Input::bindToJson(Bind const& bind) {
else if (auto controllerBind = bind.ptr<ControllerBind>()) { else if (auto controllerBind = bind.ptr<ControllerBind>()) {
return JsonObject{ return JsonObject{
{"type", "controller"}, {"type", "controller"},
{"value", ControllerButtonNames.getRight(controllerBind->button)} {"value", ControllerButtonNames.getRight(controllerBind->button)},
{"controller", controllerBind->controller}
}; };
} }
@ -392,7 +418,8 @@ bool Input::handleInput(InputEvent const& input, bool gameProcessed) {
m_bindStates[bind].press(); m_bindStates[bind].press();
} }
} }
} else if (auto keyUp = input.ptr<KeyUpEvent>()) { }
else if (auto keyUp = input.ptr<KeyUpEvent>()) {
auto keyToMod = KeysToMods.rightPtr(keyUp->key); auto keyToMod = KeysToMods.rightPtr(keyUp->key);
if (keyToMod) if (keyToMod)
m_pressedMods &= ~*keyToMod; m_pressedMods &= ~*keyToMod;
@ -410,7 +437,8 @@ bool Input::handleInput(InputEvent const& input, bool gameProcessed) {
state->release(); state->release();
} }
} }
} else if (auto mouseDown = input.ptr<MouseButtonDownEvent>()) { }
else if (auto mouseDown = input.ptr<MouseButtonDownEvent>()) {
m_mousePosition = mouseDown->mousePosition; m_mousePosition = mouseDown->mousePosition;
if (!gameProcessed) { if (!gameProcessed) {
auto& state = m_mouseStates[mouseDown->mouseButton]; auto& state = m_mouseStates[mouseDown->mouseButton];
@ -422,7 +450,8 @@ bool Input::handleInput(InputEvent const& input, bool gameProcessed) {
m_bindStates[bind].press(); m_bindStates[bind].press();
} }
} }
} else if (auto mouseUp = input.ptr<MouseButtonUpEvent>()) { }
else if (auto mouseUp = input.ptr<MouseButtonUpEvent>()) {
m_mousePosition = mouseUp->mousePosition; m_mousePosition = mouseUp->mousePosition;
if (auto state = m_mouseStates.ptr(mouseUp->mouseButton)) { if (auto state = m_mouseStates.ptr(mouseUp->mouseButton)) {
state->releasePositions.append(mouseUp->mousePosition); state->releasePositions.append(mouseUp->mousePosition);
@ -439,6 +468,28 @@ bool Input::handleInput(InputEvent const& input, bool gameProcessed) {
else if (auto mouseMove = input.ptr<MouseMoveEvent>()) { else if (auto mouseMove = input.ptr<MouseMoveEvent>()) {
m_mousePosition = mouseMove->mousePosition; m_mousePosition = mouseMove->mousePosition;
} }
else if (auto controllerDown = input.ptr<ControllerButtonDownEvent>()) {
if (!gameProcessed) {
auto& state = m_controllerStates[controllerDown->controllerButton];
state.press();
if (auto binds = m_bindMappings.ptr(controllerDown->controllerButton)) {
for (auto bind : filterBindEntries(*binds, m_pressedMods))
m_bindStates[bind].press();
}
}
}
else if (auto controllerUp = input.ptr<ControllerButtonUpEvent>()) {
if (auto state = m_controllerStates.ptr(controllerUp->controllerButton))
state->release();
if (auto binds = m_bindMappings.ptr(controllerUp->controllerButton)) {
for (auto& bind : *binds) {
if (auto state = m_bindStates.ptr(bind.entry))
state->release();
}
}
}
return false; return false;
} }

View File

@ -80,7 +80,7 @@ MaterialRenderProfile parseMaterialRenderProfile(Json const& spec, String const&
bool lightTransparent = spec.getBool("lightTransparent", false); bool lightTransparent = spec.getBool("lightTransparent", false);
profile.foregroundLightTransparent = spec.getBool("foregroundLightTransparent", lightTransparent); profile.foregroundLightTransparent = spec.getBool("foregroundLightTransparent", lightTransparent);
profile.backgroundLightTransparent = spec.getBool("backgroundLightTransparent", lightTransparent); profile.backgroundLightTransparent = spec.getBool("backgroundLightTransparent", lightTransparent);
profile.colorVariants = spec.getBool("multiColored", false) ? spec.getUInt("colorVariants", MaxMaterialColorVariant) : 0; profile.colorVariants = spec.getBool("multiColored", false) ? spec.getUInt("colorVariants", (uint64_t)MaxMaterialColorVariant + 1) : 0;
for (auto& entry : spec.getArray("colorDirectives", JsonArray())) for (auto& entry : spec.getArray("colorDirectives", JsonArray()))
profile.colorDirectives.append(entry.toString()); profile.colorDirectives.append(entry.toString());
profile.occludesBehind = spec.getBool("occludesBelow", true); profile.occludesBehind = spec.getBool("occludesBelow", true);
@ -128,14 +128,12 @@ MaterialRenderProfile parseMaterialRenderProfile(Json const& spec, String const&
auto flipTextureCoordinates = [imageHeight]( auto flipTextureCoordinates = [imageHeight](
RectF const& rect) { return RectF::withSize(Vec2F(rect.xMin(), imageHeight - rect.yMax()), rect.size()); }; RectF const& rect) { return RectF::withSize(Vec2F(rect.xMin(), imageHeight - rect.yMax()), rect.size()); };
for (unsigned v = 0; v < variants; ++v) { for (unsigned v = 0; v < variants; ++v) {
if (profile.colorVariants > 0) { auto i = DefaultMaterialColorVariant;
for (MaterialColorVariant c = 0; c <= profile.colorVariants; ++c) { RectF textureRect = RectF::withSize(texturePosition + variantStride * v, textureSize);
RectF textureRect = RectF::withSize(texturePosition + variantStride * v + colorStride * c, textureSize); renderPiece->variants[i].append(flipTextureCoordinates(textureRect));
renderPiece->variants[c].append(flipTextureCoordinates(textureRect)); for (MaterialColorVariant c = 0; c != profile.colorVariants; ++c) {
} RectF textureRect = RectF::withSize(texturePosition + variantStride * v + colorStride * ++i, textureSize);
} else { renderPiece->variants[i].append(flipTextureCoordinates(textureRect));
RectF textureRect = RectF::withSize(texturePosition + variantStride * v, textureSize);
renderPiece->variants[DefaultMaterialColorVariant].append(flipTextureCoordinates(textureRect));
} }
} }

View File

@ -66,6 +66,9 @@ Maybe<PacketStats> PacketSocket::outgoingStats() const {
void PacketSocket::setLegacy(bool legacy) { m_legacy = legacy; } void PacketSocket::setLegacy(bool legacy) { m_legacy = legacy; }
bool PacketSocket::legacy() const { return m_legacy; } bool PacketSocket::legacy() const { return m_legacy; }
void CompressedPacketSocket::setCompressionStreamEnabled(bool enabled) { m_useCompressionStream = enabled; }
bool CompressedPacketSocket::compressionStreamEnabled() const { return m_useCompressionStream; }
pair<LocalPacketSocketUPtr, LocalPacketSocketUPtr> LocalPacketSocket::openPair() { pair<LocalPacketSocketUPtr, LocalPacketSocketUPtr> LocalPacketSocket::openPair() {
auto lhsIncomingPipe = make_shared<Pipe>(); auto lhsIncomingPipe = make_shared<Pipe>();
auto rhsIncomingPipe = make_shared<Pipe>(); auto rhsIncomingPipe = make_shared<Pipe>();
@ -146,7 +149,7 @@ void TcpPacketSocket::close() {
void TcpPacketSocket::sendPackets(List<PacketPtr> packets) { void TcpPacketSocket::sendPackets(List<PacketPtr> packets) {
auto it = makeSMutableIterator(packets); auto it = makeSMutableIterator(packets);
if (m_useCompressionStream) { if (compressionStreamEnabled()) {
DataStreamBuffer outBuffer; DataStreamBuffer outBuffer;
while (it.hasNext()) { while (it.hasNext()) {
PacketPtr& packet = it.next(); PacketPtr& packet = it.next();
@ -233,7 +236,7 @@ List<PacketPtr> TcpPacketSocket::receivePackets() {
if (packetSize > ds.remaining()) if (packetSize > ds.remaining())
break; break;
m_incomingStats.mix(packetType, packetSize, !m_useCompressionStream); m_incomingStats.mix(packetType, packetSize, !compressionStreamEnabled());
DataStreamExternalBuffer packetStream(ds.ptr() + ds.pos(), packetSize); DataStreamExternalBuffer packetStream(ds.ptr() + ds.pos(), packetSize);
ByteArray uncompressed; ByteArray uncompressed;
@ -280,17 +283,17 @@ bool TcpPacketSocket::writeData() {
bool dataSent = false; bool dataSent = false;
try { try {
if (!m_outputBuffer.empty()) { if (!m_outputBuffer.empty()) {
if (m_useCompressionStream) { if (compressionStreamEnabled()) {
auto compressed = m_compressionStream.compress(m_outputBuffer); auto compressedBuffer = m_compressionStream.compress(m_outputBuffer);
m_outputBuffer.clear(); m_outputBuffer.clear();
do {
m_compressedBuffer.append(compressed.ptr(), compressed.size()); size_t written = m_socket->send(compressedBuffer.ptr(), compressedBuffer.size());
size_t written = m_socket->send(m_compressedBuffer.ptr(), m_compressedBuffer.size()); if (written > 0) {
if (written > 0) { dataSent = true;
dataSent = true; compressedBuffer.trimLeft(written);
m_compressedBuffer.trimLeft(written); m_outgoingStats.mix(written);
m_outgoingStats.mix(written); }
} } while (!compressedBuffer.empty());
} else { } else {
do { do {
size_t written = m_socket->send(m_outputBuffer.ptr(), m_outputBuffer.size()); size_t written = m_socket->send(m_outputBuffer.ptr(), m_outputBuffer.size());
@ -319,10 +322,10 @@ bool TcpPacketSocket::readData() {
if (readAmount == 0) if (readAmount == 0)
break; break;
dataReceived = true; dataReceived = true;
if (m_useCompressionStream) { if (compressionStreamEnabled()) {
m_incomingStats.mix(readAmount); m_incomingStats.mix(readAmount);
auto decompressed = m_decompressionStream.decompress(readBuffer, readAmount); auto decompressed = m_decompressionStream.decompress(readBuffer, readAmount);
m_inputBuffer.append(decompressed.ptr(), decompressed.size()); m_inputBuffer.append(decompressed);
} else { } else {
m_inputBuffer.append(readBuffer, readAmount); m_inputBuffer.append(readBuffer, readAmount);
} }
@ -345,7 +348,6 @@ Maybe<PacketStats> TcpPacketSocket::outgoingStats() const {
} }
void TcpPacketSocket::setLegacy(bool legacy) { void TcpPacketSocket::setLegacy(bool legacy) {
m_useCompressionStream = !legacy;
PacketSocket::setLegacy(legacy); PacketSocket::setLegacy(legacy);
} }
@ -366,43 +368,58 @@ void P2PPacketSocket::close() {
void P2PPacketSocket::sendPackets(List<PacketPtr> packets) { void P2PPacketSocket::sendPackets(List<PacketPtr> packets) {
auto it = makeSMutableIterator(packets); auto it = makeSMutableIterator(packets);
while (it.hasNext()) { if (compressionStreamEnabled()) {
PacketType currentType = it.peekNext()->type();
PacketCompressionMode currentCompressionMode = it.peekNext()->compressionMode();
DataStreamBuffer packetBuffer;
while (it.hasNext()
&& it.peekNext()->type() == currentType
&& it.peekNext()->compressionMode() == currentCompressionMode) {
if (legacy())
it.next()->writeLegacy(packetBuffer);
else
it.next()->write(packetBuffer);
}
// Packets must read and write actual data, because this is used to
// determine packet count
starAssert(!packetBuffer.empty());
ByteArray compressedPackets;
bool mustCompress = currentCompressionMode == PacketCompressionMode::Enabled;
bool perhapsCompress = currentCompressionMode == PacketCompressionMode::Automatic && packetBuffer.size() > 64;
if (mustCompress || perhapsCompress)
compressedPackets = compressData(packetBuffer.data());
DataStreamBuffer outBuffer; DataStreamBuffer outBuffer;
outBuffer.write(currentType); while (it.hasNext()) {
PacketType currentType = it.peekNext()->type();
if (!compressedPackets.empty() && (mustCompress || compressedPackets.size() < packetBuffer.size())) { DataStreamBuffer packetBuffer;
outBuffer.write<bool>(true); while (it.hasNext() && it.peekNext()->type() == currentType)
outBuffer.writeData(compressedPackets.ptr(), compressedPackets.size()); it.next()->write(packetBuffer);
m_outgoingStats.mix(currentType, compressedPackets.size()); outBuffer.write(currentType);
} else {
outBuffer.write<bool>(false); outBuffer.write<bool>(false);
outBuffer.writeData(packetBuffer.ptr(), packetBuffer.size()); outBuffer.writeData(packetBuffer.ptr(), packetBuffer.size());
m_outgoingStats.mix(currentType, packetBuffer.size()); m_outgoingStats.mix(currentType, packetBuffer.size(), false);
m_outputMessages.append(m_compressionStream.compress(outBuffer.takeData()));
}
} else {
while (it.hasNext()) {
PacketType currentType = it.peekNext()->type();
PacketCompressionMode currentCompressionMode = it.peekNext()->compressionMode();
DataStreamBuffer packetBuffer;
while (it.hasNext()
&& it.peekNext()->type() == currentType
&& it.peekNext()->compressionMode() == currentCompressionMode) {
if (legacy())
it.next()->writeLegacy(packetBuffer);
else
it.next()->write(packetBuffer);
}
// Packets must read and write actual data, because this is used to
// determine packet count
starAssert(!packetBuffer.empty());
ByteArray compressedPackets;
bool mustCompress = currentCompressionMode == PacketCompressionMode::Enabled;
bool perhapsCompress = currentCompressionMode == PacketCompressionMode::Automatic && packetBuffer.size() > 64;
if (mustCompress || perhapsCompress)
compressedPackets = compressData(packetBuffer.data());
DataStreamBuffer outBuffer;
outBuffer.write(currentType);
if (!compressedPackets.empty() && (mustCompress || compressedPackets.size() < packetBuffer.size())) {
outBuffer.write<bool>(true);
outBuffer.writeData(compressedPackets.ptr(), compressedPackets.size());
m_outgoingStats.mix(currentType, compressedPackets.size());
} else {
outBuffer.write<bool>(false);
outBuffer.writeData(packetBuffer.ptr(), packetBuffer.size());
m_outgoingStats.mix(currentType, packetBuffer.size());
}
m_outputMessages.append(outBuffer.takeData());
} }
m_outputMessages.append(outBuffer.takeData());
} }
} }
@ -420,9 +437,9 @@ List<PacketPtr> P2PPacketSocket::receivePackets() {
if (packetCompressed) if (packetCompressed)
packetBytes = uncompressData(packetBytes); packetBytes = uncompressData(packetBytes);
m_incomingStats.mix(packetType, packetSize); m_incomingStats.mix(packetType, packetSize, !compressionStreamEnabled());
DataStreamBuffer packetStream(std::move(packetBytes)); DataStreamExternalBuffer packetStream(packetBytes);
do { do {
PacketPtr packet = createPacket(packetType); PacketPtr packet = createPacket(packetType);
packet->setCompressionMode(packetCompressed ? PacketCompressionMode::Enabled : PacketCompressionMode::Disabled); packet->setCompressionMode(packetCompressed ? PacketCompressionMode::Enabled : PacketCompressionMode::Disabled);
@ -450,6 +467,7 @@ bool P2PPacketSocket::writeData() {
if (m_socket) { if (m_socket) {
while (!m_outputMessages.empty()) { while (!m_outputMessages.empty()) {
if (m_socket->sendMessage(m_outputMessages.first())) { if (m_socket->sendMessage(m_outputMessages.first())) {
m_outgoingStats.mix(m_outputMessages.first().size());
m_outputMessages.removeFirst(); m_outputMessages.removeFirst();
workDone = true; workDone = true;
} else { } else {
@ -466,7 +484,10 @@ bool P2PPacketSocket::readData() {
if (m_socket) { if (m_socket) {
while (auto message = m_socket->receiveMessage()) { while (auto message = m_socket->receiveMessage()) {
m_inputMessages.append(message.take()); m_incomingStats.mix(message->size());
m_inputMessages.append(compressionStreamEnabled()
? m_decompressionStream.decompress(*message)
: *message);
workDone = true; workDone = true;
} }
} }

View File

@ -78,7 +78,20 @@ public:
virtual void setLegacy(bool legacy); virtual void setLegacy(bool legacy);
virtual bool legacy() const; virtual bool legacy() const;
private: private:
bool m_legacy = true; bool m_legacy = false;
};
class CompressedPacketSocket : public PacketSocket {
public:
virtual ~CompressedPacketSocket() = default;
virtual void setCompressionStreamEnabled(bool enabled);
virtual bool compressionStreamEnabled() const;
private:
bool m_useCompressionStream = false;
protected:
CompressionStream m_compressionStream;
DecompressionStream m_decompressionStream;
}; };
// PacketSocket for local communication. // PacketSocket for local communication.
@ -112,7 +125,7 @@ private:
}; };
// Wraps a TCP socket into a PacketSocket. // Wraps a TCP socket into a PacketSocket.
class TcpPacketSocket : public PacketSocket { class TcpPacketSocket : public CompressedPacketSocket {
public: public:
static TcpPacketSocketUPtr open(TcpSocketPtr socket); static TcpPacketSocketUPtr open(TcpSocketPtr socket);
@ -140,14 +153,10 @@ private:
PacketStatCollector m_outgoingStats; PacketStatCollector m_outgoingStats;
ByteArray m_outputBuffer; ByteArray m_outputBuffer;
ByteArray m_inputBuffer; ByteArray m_inputBuffer;
bool m_useCompressionStream = false;
ByteArray m_compressedBuffer;
CompressionStream m_compressionStream;
DecompressionStream m_decompressionStream;
}; };
// Wraps a P2PSocket into a PacketSocket // Wraps a P2PSocket into a PacketSocket
class P2PPacketSocket : public PacketSocket { class P2PPacketSocket : public CompressedPacketSocket {
public: public:
static P2PPacketSocketUPtr open(P2PSocketUPtr socket); static P2PPacketSocketUPtr open(P2PSocketUPtr socket);

View File

@ -78,6 +78,11 @@ EnumMap<PacketType> const PacketTypeNames{
{PacketType::SystemObjectSpawn, "SystemObjectSpawn"} {PacketType::SystemObjectSpawn, "SystemObjectSpawn"}
}; };
EnumMap<NetCompressionMode> const NetCompressionModeNames {
{NetCompressionMode::None, "None"},
{NetCompressionMode::Zstd, "Zstd"}
};
Packet::~Packet() {} Packet::~Packet() {}
void Packet::readLegacy(DataStream& ds) { read(ds); } void Packet::readLegacy(DataStream& ds) { read(ds); }
@ -187,15 +192,27 @@ void ProtocolRequestPacket::write(DataStream& ds) const {
ds.write(requestProtocolVersion); ds.write(requestProtocolVersion);
} }
ProtocolResponsePacket::ProtocolResponsePacket(bool allowed) ProtocolResponsePacket::ProtocolResponsePacket(bool allowed, Json info)
: allowed(allowed) {} : allowed(allowed), info(info) {}
void ProtocolResponsePacket::read(DataStream& ds) { void ProtocolResponsePacket::read(DataStream& ds) {
ds.read(allowed); ds.read(allowed);
if (compressionMode() == PacketCompressionMode::Enabled) {
// gross hack for backwards compatibility with older OpenSB servers
// can be removed later
auto externalBuffer = as<DataStreamExternalBuffer>(&ds);
if (!externalBuffer || !externalBuffer->atEnd())
ds.read(info);
}
}
void ProtocolResponsePacket::writeLegacy(DataStream& ds) const {
ds.write(allowed);
} }
void ProtocolResponsePacket::write(DataStream& ds) const { void ProtocolResponsePacket::write(DataStream& ds) const {
ds.write(allowed); writeLegacy(ds);
ds.write(info);
} }
ConnectSuccessPacket::ConnectSuccessPacket() {} ConnectSuccessPacket::ConnectSuccessPacket() {}
@ -387,10 +404,10 @@ ClientConnectPacket::ClientConnectPacket() {}
ClientConnectPacket::ClientConnectPacket(ByteArray assetsDigest, bool allowAssetsMismatch, Uuid playerUuid, ClientConnectPacket::ClientConnectPacket(ByteArray assetsDigest, bool allowAssetsMismatch, Uuid playerUuid,
String playerName, String playerSpecies, WorldChunks shipChunks, ShipUpgrades shipUpgrades, String playerName, String playerSpecies, WorldChunks shipChunks, ShipUpgrades shipUpgrades,
bool introComplete, String account) bool introComplete, String account, Json info)
: assetsDigest(std::move(assetsDigest)), allowAssetsMismatch(allowAssetsMismatch), playerUuid(std::move(playerUuid)), : assetsDigest(std::move(assetsDigest)), allowAssetsMismatch(allowAssetsMismatch), playerUuid(std::move(playerUuid)),
playerName(std::move(playerName)), playerSpecies(std::move(playerSpecies)), shipChunks(std::move(shipChunks)), playerName(std::move(playerName)), playerSpecies(std::move(playerSpecies)), shipChunks(std::move(shipChunks)),
shipUpgrades(std::move(shipUpgrades)), introComplete(std::move(introComplete)), account(std::move(account)) {} shipUpgrades(std::move(shipUpgrades)), introComplete(std::move(introComplete)), account(std::move(account)), info(std::move(info)) {}
void ClientConnectPacket::readLegacy(DataStream& ds) { void ClientConnectPacket::readLegacy(DataStream& ds) {
ds.read(assetsDigest); ds.read(assetsDigest);
@ -402,6 +419,7 @@ void ClientConnectPacket::readLegacy(DataStream& ds) {
ds.read(shipUpgrades); ds.read(shipUpgrades);
ds.read(introComplete); ds.read(introComplete);
ds.read(account); ds.read(account);
info = Json();
} }
void ClientConnectPacket::read(DataStream& ds) { void ClientConnectPacket::read(DataStream& ds) {

View File

@ -116,10 +116,16 @@ enum class PacketType : uint8_t {
}; };
extern EnumMap<PacketType> const PacketTypeNames; extern EnumMap<PacketType> const PacketTypeNames;
enum class NetCompressionMode : uint8_t {
None,
Zstd
};
extern EnumMap<NetCompressionMode> const NetCompressionModeNames;
enum class PacketCompressionMode : uint8_t { enum class PacketCompressionMode : uint8_t {
Disabled, Disabled,
Enabled, Automatic,
Automatic Enabled
}; };
struct Packet { struct Packet {
@ -162,12 +168,14 @@ struct ProtocolRequestPacket : PacketBase<PacketType::ProtocolRequest> {
}; };
struct ProtocolResponsePacket : PacketBase<PacketType::ProtocolResponse> { struct ProtocolResponsePacket : PacketBase<PacketType::ProtocolResponse> {
ProtocolResponsePacket(bool allowed = false); ProtocolResponsePacket(bool allowed = false, Json info = {});
void read(DataStream& ds) override; void read(DataStream& ds) override;
void writeLegacy(DataStream& ds) const override;
void write(DataStream& ds) const override; void write(DataStream& ds) const override;
bool allowed; bool allowed;
Json info;
}; };
struct ServerDisconnectPacket : PacketBase<PacketType::ServerDisconnect> { struct ServerDisconnectPacket : PacketBase<PacketType::ServerDisconnect> {
@ -302,7 +310,7 @@ struct ClientConnectPacket : PacketBase<PacketType::ClientConnect> {
ClientConnectPacket(); ClientConnectPacket();
ClientConnectPacket(ByteArray assetsDigest, bool allowAssetsMismatch, Uuid playerUuid, String playerName, ClientConnectPacket(ByteArray assetsDigest, bool allowAssetsMismatch, Uuid playerUuid, String playerName,
String playerSpecies, WorldChunks shipChunks, ShipUpgrades shipUpgrades, bool introComplete, String playerSpecies, WorldChunks shipChunks, ShipUpgrades shipUpgrades, bool introComplete,
String account); String account, Json info = {});
void readLegacy(DataStream& ds) override; void readLegacy(DataStream& ds) override;
void read(DataStream& ds) override; void read(DataStream& ds) override;

View File

@ -110,6 +110,8 @@ Object::Object(ObjectConfigConstPtr config, Json const& parameters) {
m_netGroup.setNeedsLoadCallback(bind(&Object::getNetStates, this, _1)); m_netGroup.setNeedsLoadCallback(bind(&Object::getNetStates, this, _1));
m_netGroup.setNeedsStoreCallback(bind(&Object::setNetStates, this)); m_netGroup.setNeedsStoreCallback(bind(&Object::setNetStates, this));
m_clientEntityMode = ClientEntityModeNames.getLeft(configValue("clientEntityMode", "ClientSlaveOnly").toString());
} }
Json Object::diskStore() const { Json Object::diskStore() const {
@ -127,6 +129,10 @@ EntityType Object::entityType() const {
return EntityType::Object; return EntityType::Object;
} }
ClientEntityMode Object::clientEntityMode() const {
return m_clientEntityMode;
}
void Object::init(World* world, EntityId entityId, EntityMode mode) { void Object::init(World* world, EntityId entityId, EntityMode mode) {
Entity::init(world, entityId, mode); Entity::init(world, entityId, mode);
// Only try and find a new orientation if we do not already have one, // Only try and find a new orientation if we do not already have one,
@ -182,7 +188,11 @@ void Object::init(World* world, EntityId entityId, EntityMode mode) {
setKeepAlive(configValue("keepAlive", false).toBool()); setKeepAlive(configValue("keepAlive", false).toBool());
m_scriptComponent.setScripts(m_config->scripts); auto jScripts = configValue("scripts", JsonArray());
if (jScripts.isType(Json::Type::Array))
m_scriptComponent.setScripts(jsonToStringList(jScripts).transformed(bind(AssetPath::relativeTo, m_config->path, _1)));
else
m_scriptComponent.setScripts(m_config->scripts);
m_scriptComponent.setUpdateDelta(configValue("scriptDelta", 5).toInt()); m_scriptComponent.setUpdateDelta(configValue("scriptDelta", 5).toInt());
m_scriptComponent.addCallbacks("object", makeObjectCallbacks()); m_scriptComponent.addCallbacks("object", makeObjectCallbacks());

View File

@ -40,6 +40,7 @@ public:
ByteArray netStore(); ByteArray netStore();
virtual EntityType entityType() const override; virtual EntityType entityType() const override;
virtual ClientEntityMode clientEntityMode() const override;
virtual void init(World* world, EntityId entityId, EntityMode mode) override; virtual void init(World* world, EntityId entityId, EntityMode mode) override;
virtual void uninit() override; virtual void uninit() override;
@ -267,6 +268,8 @@ private:
NetElementHashMap<String, Json> m_scriptedAnimationParameters; NetElementHashMap<String, Json> m_scriptedAnimationParameters;
NetElementData<List<DamageSource>> m_damageSources; NetElementData<List<DamageSource>> m_damageSources;
ClientEntityMode m_clientEntityMode;
}; };
} }

View File

@ -976,15 +976,21 @@ void Player::update(float dt, uint64_t) {
}); });
} }
for (auto tool : {m_tools->primaryHandItem(), m_tools->altHandItem()}) { for (auto& tool : {m_tools->primaryHandItem(), m_tools->altHandItem()}) {
if (auto inspectionTool = as<InspectionTool>(tool)) { if (auto inspectionTool = as<InspectionTool>(tool)) {
for (auto ir : inspectionTool->pullInspectionResults()) { for (auto& ir : inspectionTool->pullInspectionResults()) {
if (ir.objectName) { if (ir.objectName) {
m_questManager->receiveMessage("objectScanned", true, {*ir.objectName, *ir.entityId}); m_questManager->receiveMessage("objectScanned", true, {*ir.objectName, *ir.entityId});
m_log->addScannedObject(*ir.objectName); m_log->addScannedObject(*ir.objectName);
} }
addChatMessage(ir.message); addChatMessage(ir.message, JsonObject{
{"message", JsonObject{
{"context", JsonObject{{"mode", "RadioMessage"}}},
{"fromConnection", world()->connection()},
{"text", ir.message}
}}
});
} }
} }
} }
@ -2178,12 +2184,12 @@ void Player::queueItemPickupMessage(ItemPtr const& item) {
m_queuedItemPickups.append(item); m_queuedItemPickups.append(item);
} }
void Player::addChatMessage(String const& message) { void Player::addChatMessage(String const& message, Json const& config) {
starAssert(!isSlave()); starAssert(!isSlave());
m_chatMessage = message; m_chatMessage = message;
m_chatMessageUpdated = true; m_chatMessageUpdated = true;
m_chatMessageChanged = true; m_chatMessageChanged = true;
m_pendingChatActions.append(SayChatAction{entityId(), message, mouthPosition()}); m_pendingChatActions.append(SayChatAction{entityId(), message, mouthPosition(), config});
} }
void Player::addEmote(HumanoidEmote const& emote, Maybe<float> emoteCooldown) { void Player::addEmote(HumanoidEmote const& emote, Maybe<float> emoteCooldown) {
@ -2196,6 +2202,10 @@ pair<HumanoidEmote, float> Player::currentEmote() const {
return make_pair(m_emoteState, m_emoteCooldownTimer); return make_pair(m_emoteState, m_emoteCooldownTimer);
} }
Player::State Player::currentState() const {
return m_state;
}
List<ChatAction> Player::pullPendingChatActions() { List<ChatAction> Player::pullPendingChatActions() {
return take(m_pendingChatActions); return take(m_pendingChatActions);
} }

View File

@ -60,6 +60,21 @@ class Player :
public virtual EmoteEntity { public virtual EmoteEntity {
public: public:
enum class State {
Idle,
Walk,
Run,
Jump,
Fall,
Swim,
SwimIdle,
TeleportIn,
TeleportOut,
Crouch,
Lounge
};
static EnumMap<State> const StateNames;
Player(PlayerConfigPtr config, Uuid uuid = Uuid()); Player(PlayerConfigPtr config, Uuid uuid = Uuid());
Player(PlayerConfigPtr config, ByteArray const& netStore); Player(PlayerConfigPtr config, ByteArray const& netStore);
Player(PlayerConfigPtr config, Json const& diskStore); Player(PlayerConfigPtr config, Json const& diskStore);
@ -381,10 +396,12 @@ public:
void queueUIMessage(String const& message) override; void queueUIMessage(String const& message) override;
void queueItemPickupMessage(ItemPtr const& item); void queueItemPickupMessage(ItemPtr const& item);
void addChatMessage(String const& message); void addChatMessage(String const& message, Json const& config = {});
void addEmote(HumanoidEmote const& emote, Maybe<float> emoteCooldown = {}); void addEmote(HumanoidEmote const& emote, Maybe<float> emoteCooldown = {});
pair<HumanoidEmote, float> currentEmote() const; pair<HumanoidEmote, float> currentEmote() const;
State currentState() const;
List<ChatAction> pullPendingChatActions() override; List<ChatAction> pullPendingChatActions() override;
Maybe<String> inspectionLogName() const override; Maybe<String> inspectionLogName() const override;
@ -483,21 +500,6 @@ public:
void setSecretProperty(String const& name, Json const& value); void setSecretProperty(String const& name, Json const& value);
private: private:
enum class State {
Idle,
Walk,
Run,
Jump,
Fall,
Swim,
SwimIdle,
TeleportIn,
TeleportOut,
Crouch,
Lounge
};
static EnumMap<State> const StateNames;
typedef LuaMessageHandlingComponent<LuaStorableComponent<LuaActorMovementComponent<LuaUpdatableComponent<LuaWorldComponent<LuaBaseComponent>>>>> GenericScriptComponent; typedef LuaMessageHandlingComponent<LuaStorableComponent<LuaActorMovementComponent<LuaUpdatableComponent<LuaWorldComponent<LuaBaseComponent>>>>> GenericScriptComponent;
typedef shared_ptr<GenericScriptComponent> GenericScriptComponentPtr; typedef shared_ptr<GenericScriptComponent> GenericScriptComponentPtr;

View File

@ -1008,12 +1008,15 @@ ItemPtr& PlayerInventory::retrieve(InventorySlot const& slot) {
if (auto es = slot.ptr<EquipmentSlot>()) if (auto es = slot.ptr<EquipmentSlot>())
return guardEmpty(m_equipment[*es]); return guardEmpty(m_equipment[*es]);
else if (auto bs = slot.ptr<BagSlot>()) else if (auto bs = slot.ptr<BagSlot>()) {
return guardEmpty(m_bags[bs->first]->at(bs->second)); if (auto bag = m_bags.ptr(bs->first))
else if (slot.is<SwapSlot>()) return guardEmpty((*bag)->at(bs->second));
} else if (slot.is<SwapSlot>())
return guardEmpty(m_swapSlot); return guardEmpty(m_swapSlot);
else else
return guardEmpty(m_trashSlot); return guardEmpty(m_trashSlot);
throw ItemException::format("Invalid inventory slot {}", jsonFromInventorySlot(slot));
} }
void PlayerInventory::swapCustomBarLinks(InventorySlot a, InventorySlot b) { void PlayerInventory::swapCustomBarLinks(InventorySlot a, InventorySlot b) {

View File

@ -71,30 +71,30 @@ List<pair<Vec3I, OrbitBookmark>> PlayerUniverseMap::orbitBookmarks() const {
return bookmarks; return bookmarks;
} }
void PlayerUniverseMap::addOrbitBookmark(CelestialCoordinate const& system, OrbitBookmark const& bookmark) { bool PlayerUniverseMap::addOrbitBookmark(CelestialCoordinate const& system, OrbitBookmark const& bookmark) {
if (system.isNull()) if (system.isNull())
throw StarException("Cannot add orbit bookmark to null system"); throw StarException("Cannot add orbit bookmark to null system");
m_universeMaps[*m_serverUuid].systems[system.location()].bookmarks.add(std::move(bookmark)); return m_universeMaps[*m_serverUuid].systems[system.location()].bookmarks.add(std::move(bookmark));
} }
void PlayerUniverseMap::removeOrbitBookmark(CelestialCoordinate const& system, OrbitBookmark const& bookmark) { bool PlayerUniverseMap::removeOrbitBookmark(CelestialCoordinate const& system, OrbitBookmark const& bookmark) {
if (system.isNull()) if (system.isNull())
throw StarException("Cannot remove orbit bookmark from null system"); throw StarException("Cannot remove orbit bookmark from null system");
m_universeMaps[*m_serverUuid].systems[system.location()].bookmarks.remove(bookmark); return m_universeMaps[*m_serverUuid].systems[system.location()].bookmarks.remove(bookmark);
} }
List<TeleportBookmark> PlayerUniverseMap::teleportBookmarks() const { List<TeleportBookmark> PlayerUniverseMap::teleportBookmarks() const {
return universeMap().teleportBookmarks.values(); return universeMap().teleportBookmarks.values();
} }
void PlayerUniverseMap::addTeleportBookmark(TeleportBookmark bookmark) { bool PlayerUniverseMap::addTeleportBookmark(TeleportBookmark bookmark) {
m_universeMaps[*m_serverUuid].teleportBookmarks.add(std::move(bookmark)); return m_universeMaps[*m_serverUuid].teleportBookmarks.add(std::move(bookmark));
} }
void PlayerUniverseMap::removeTeleportBookmark(TeleportBookmark const& bookmark) { bool PlayerUniverseMap::removeTeleportBookmark(TeleportBookmark const& bookmark) {
m_universeMaps[*m_serverUuid].teleportBookmarks.remove(bookmark); return m_universeMaps[*m_serverUuid].teleportBookmarks.remove(bookmark);
} }
void PlayerUniverseMap::invalidateWarpAction(WarpAction const& warpAction) { void PlayerUniverseMap::invalidateWarpAction(WarpAction const& warpAction) {

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