Bringing over more C++ sources

This commit is contained in:
Eric Froemling 2020-10-13 15:07:59 -07:00
parent de4ccee2fb
commit 469708d4f2
10 changed files with 1100 additions and 20 deletions

View File

@ -3932,24 +3932,24 @@
"assets/build/windows/Win32/ucrtbased.dll": "https://files.ballistica.net/cache/ba1/b5/85/f8b6d0558ddb87267f34254b1450",
"assets/build/windows/Win32/vc_redist.x86.exe": "https://files.ballistica.net/cache/ba1/1c/e1/4a1a2eddda2f4aebd5f8b64ab08e",
"assets/build/windows/Win32/vcruntime140d.dll": "https://files.ballistica.net/cache/ba1/50/8d/bc2600ac9491f1b14d659709451f",
"build/prefab/full/linux_x86_64/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/13/a0/ef123d2da6dadcf719db687a7f2b",
"build/prefab/full/linux_x86_64/release/ballisticacore": "https://files.ballistica.net/cache/ba1/70/35/5b8bb7d54fc172c269940562b6ad",
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/8c/51/a79b59f13bb601147766411ede9b",
"build/prefab/full/linux_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/0f/75/944ebd95803210c4fd76ae8fb987",
"build/prefab/full/mac_x86_64/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/05/87/7f0670d132417bb3ab90697afcf1",
"build/prefab/full/mac_x86_64/release/ballisticacore": "https://files.ballistica.net/cache/ba1/3d/66/e28afa2246fef0f079b2e6e25d45",
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/42/4a/3ab2b7025e66a942c63974ea74d7",
"build/prefab/full/mac_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/01/28/1690f5728849db47a4db0badba4a",
"build/prefab/full/windows_x86/debug/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/f9/8a/72522f7d2d6a9884fd19f7ab266b",
"build/prefab/full/windows_x86/release/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/84/a5/7c0050e6fdf0c9059a7dbff94551",
"build/prefab/full/windows_x86_server/debug/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/01/a8/f25da7a3e3e6db52e8504672952c",
"build/prefab/full/windows_x86_server/release/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/82/21/261b3ba6d012a02386e425fc1257",
"build/prefab/lib/linux_x86_64/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/22/0a/572910a668aeb607f6e6d9544266",
"build/prefab/lib/linux_x86_64/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/e1/1a/ff5b7abc008154a71152a1635d33",
"build/prefab/lib/linux_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/e6/93/af621822f3d5d6021efecac32235",
"build/prefab/lib/linux_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/3e/62/cc942c068c685832f6b7664bbb0f",
"build/prefab/lib/mac_x86_64/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/68/1a/8e04067c191229b1dcfb6a470ec1",
"build/prefab/lib/mac_x86_64/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/dd/1f/646dd2c8f69f3d123aa02de7ec5c",
"build/prefab/lib/mac_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/28/45/922dc5d0c0b61d252355cf09997d",
"build/prefab/lib/mac_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/41/25/842bf633e2f05dd3c037460064ab"
"build/prefab/full/linux_x86_64/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/b8/eb/280d15f31dcbf3830e6730f9c4be",
"build/prefab/full/linux_x86_64/release/ballisticacore": "https://files.ballistica.net/cache/ba1/b9/b6/7a4d577d71bbce1d0a42d0fa8f0e",
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/64/46/406adb6907d84ab1fbce0238b2f5",
"build/prefab/full/linux_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/e2/4a/c3ab9dfa2eb438e2270c867a0a32",
"build/prefab/full/mac_x86_64/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/75/7d/64dee3a33a9b9df3213789d031ca",
"build/prefab/full/mac_x86_64/release/ballisticacore": "https://files.ballistica.net/cache/ba1/24/4e/0de9a93c5abf5f91efa893c0f657",
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/ba/e3/7c54b7b121a73945a334acf2a778",
"build/prefab/full/mac_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/8b/80/59caf9ded1ac2d852f57489049c2",
"build/prefab/full/windows_x86/debug/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/72/b8/b24e6069245d0c3e7c1f049e1386",
"build/prefab/full/windows_x86/release/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/79/62/ad715d74684b940ce846e7e58d8c",
"build/prefab/full/windows_x86_server/debug/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/23/41/552a8ffeef5bc10bd0af04c10565",
"build/prefab/full/windows_x86_server/release/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/bf/aa/1dcfd60aeaec8d42f06adac2156b",
"build/prefab/lib/linux_x86_64/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/a5/db/bcc616e930ef02de5f086cc819a1",
"build/prefab/lib/linux_x86_64/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/7f/3b/af2f0efe5996de7cfc31a112c514",
"build/prefab/lib/linux_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/8f/22/e3bb8333289707bba87db62cff1b",
"build/prefab/lib/linux_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/93/88/21434630ae75dfd78e8f1225beaf",
"build/prefab/lib/mac_x86_64/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/3c/2c/317eabff84c4b1819aeb78dc984a",
"build/prefab/lib/mac_x86_64/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/12/b2/e0851ca040064fb5c4d953003a10",
"build/prefab/lib/mac_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/15/88/cfef2301d0ff44a26c411ed11c39",
"build/prefab/lib/mac_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/b2/7a/5c80fa986d5da7908d7c3c31c61f"
}

View File

@ -174,10 +174,15 @@ add_executable(ballisticacore
# AUTOGENERATED_PUBLIC_BEGIN (this section is managed by the "update_project" tool)
${BA_SRC_ROOT}/ballistica/app/app.cc
${BA_SRC_ROOT}/ballistica/app/app.h
${BA_SRC_ROOT}/ballistica/app/app_config.cc
${BA_SRC_ROOT}/ballistica/app/app_config.h
${BA_SRC_ROOT}/ballistica/app/app_globals.cc
${BA_SRC_ROOT}/ballistica/app/app_globals.h
${BA_SRC_ROOT}/ballistica/app/headless_app.cc
${BA_SRC_ROOT}/ballistica/app/headless_app.h
${BA_SRC_ROOT}/ballistica/app/stress_test.cc
${BA_SRC_ROOT}/ballistica/app/stress_test.h
${BA_SRC_ROOT}/ballistica/app/vr_app.cc
${BA_SRC_ROOT}/ballistica/app/vr_app.h
${BA_SRC_ROOT}/ballistica/audio/al_sys.cc
${BA_SRC_ROOT}/ballistica/audio/al_sys.h
@ -277,7 +282,9 @@ add_executable(ballisticacore
${BA_SRC_ROOT}/ballistica/game/game.h
${BA_SRC_ROOT}/ballistica/game/game_stream.h
${BA_SRC_ROOT}/ballistica/game/host_activity.h
${BA_SRC_ROOT}/ballistica/game/player.cc
${BA_SRC_ROOT}/ballistica/game/player.h
${BA_SRC_ROOT}/ballistica/game/player_spec.cc
${BA_SRC_ROOT}/ballistica/game/player_spec.h
${BA_SRC_ROOT}/ballistica/game/score_to_beat.h
${BA_SRC_ROOT}/ballistica/game/session/client_session.h
@ -453,6 +460,7 @@ add_executable(ballisticacore
${BA_SRC_ROOT}/ballistica/networking/network_write_module.h
${BA_SRC_ROOT}/ballistica/networking/networking.h
${BA_SRC_ROOT}/ballistica/networking/networking_sys.h
${BA_SRC_ROOT}/ballistica/networking/sockaddr.cc
${BA_SRC_ROOT}/ballistica/networking/sockaddr.h
${BA_SRC_ROOT}/ballistica/networking/telnet_server.cc
${BA_SRC_ROOT}/ballistica/networking/telnet_server.h

View File

@ -0,0 +1,245 @@
// Released under the MIT License. See LICENSE for details.
#include "ballistica/app/app_config.h"
#include <utility>
#include "ballistica/ballistica.h"
#include "ballistica/platform/platform.h"
#include "ballistica/python/python.h"
namespace ballistica {
void AppConfig::Init() { new AppConfig(); }
auto AppConfig::Entry::FloatValue() const -> float {
throw Exception("not a float entry");
}
auto AppConfig::Entry::StringValue() const -> std::string {
throw Exception("not a string entry");
}
auto AppConfig::Entry::IntValue() const -> int {
throw Exception("not an int entry");
}
auto AppConfig::Entry::BoolValue() const -> bool {
throw Exception("not a bool entry");
}
auto AppConfig::Entry::DefaultFloatValue() const -> float {
throw Exception("not a float entry");
}
auto AppConfig::Entry::DefaultStringValue() const -> std::string {
throw Exception("not a string entry");
}
auto AppConfig::Entry::DefaultIntValue() const -> int {
throw Exception("not an int entry");
}
auto AppConfig::Entry::DefaultBoolValue() const -> bool {
throw Exception("not a bool entry");
}
class AppConfig::StringEntry : public AppConfig::Entry {
public:
StringEntry() = default;
StringEntry(const char* name, std::string default_value)
: Entry(name), default_value_(std::move(default_value)) {}
auto GetType() const -> Type override { return Type::kString; }
auto Resolve() const -> std::string {
return g_python->GetRawConfigValue(name().c_str(), default_value_.c_str());
}
auto StringValue() const -> std::string override { return Resolve(); }
auto DefaultStringValue() const -> std::string override {
return default_value_;
}
private:
std::string default_value_;
};
class AppConfig::FloatEntry : public AppConfig::Entry {
public:
FloatEntry() = default;
FloatEntry(const char* name, float default_value)
: Entry(name), default_value_(default_value) {}
auto GetType() const -> Type override { return Type::kFloat; }
auto Resolve() const -> float {
return g_python->GetRawConfigValue(name().c_str(), default_value_);
}
auto FloatValue() const -> float override { return Resolve(); }
auto DefaultFloatValue() const -> float override { return default_value_; }
private:
float default_value_{};
};
class AppConfig::IntEntry : public AppConfig::Entry {
public:
IntEntry() = default;
IntEntry(const char* name, int default_value)
: Entry(name), default_value_(default_value) {}
auto GetType() const -> Type override { return Type::kInt; }
auto Resolve() const -> int {
return g_python->GetRawConfigValue(name().c_str(), default_value_);
}
auto IntValue() const -> int override { return Resolve(); }
auto DefaultIntValue() const -> int override { return default_value_; }
private:
int default_value_{};
};
class AppConfig::BoolEntry : public AppConfig::Entry {
public:
BoolEntry() = default;
BoolEntry(const char* name, bool default_value)
: Entry(name), default_value_(default_value) {}
auto GetType() const -> Type override { return Type::kBool; }
auto Resolve() const -> bool {
return g_python->GetRawConfigValue(name().c_str(), default_value_);
}
auto BoolValue() const -> bool override { return Resolve(); }
auto DefaultBoolValue() const -> bool override { return default_value_; }
private:
bool default_value_{};
};
AppConfig::AppConfig() {
// (We're a singleton).
assert(g_app_config == nullptr);
g_app_config = this;
SetupEntries();
}
template <typename T>
void AppConfig::CompleteMap(const T& entry_map) {
for (auto&& i : entry_map) {
assert(entries_by_name_.find(i.second.name()) == entries_by_name_.end());
assert(i.first < decltype(i.first)::kLast);
entries_by_name_[i.second.name()] = &i.second;
}
// Make sure all values have entries.
#if BA_DEBUG_BUILD
int last = static_cast<int>(decltype(entry_map.begin()->first)::kLast); // ew
for (int j = 0; j < last; ++j) {
auto i2 =
entry_map.find(static_cast<decltype(entry_map.begin()->first)>(j));
if (i2 == entry_map.end()) {
throw Exception("Missing appconfig entry " + std::to_string(j));
}
}
#endif
}
void AppConfig::SetupEntries() {
// Register all our typed entries.
float_entries_[FloatID::kScreenGamma] = FloatEntry("Screen Gamma", 1.0F);
float_entries_[FloatID::kScreenPixelScale] =
FloatEntry("Screen Pixel Scale", 1.0F);
float_entries_[FloatID::kTouchControlsScale] =
FloatEntry("Touch Controls Scale", 1.0F);
float_entries_[FloatID::kTouchControlsScaleMovement] =
FloatEntry("Touch Controls Scale Movement", 1.0F);
float_entries_[FloatID::kTouchControlsScaleActions] =
FloatEntry("Touch Controls Scale Actions", 1.0F);
float_entries_[FloatID::kSoundVolume] = FloatEntry("Sound Volume", 1.0F);
float_entries_[FloatID::kMusicVolume] = FloatEntry("Music Volume", 1.0F);
// Note: keep this synced with the defaults in MainActivity.java.
float gvrrts_default = g_platform->IsRunningOnDaydream() ? 1.0F : 0.5F;
float_entries_[FloatID::kGoogleVRRenderTargetScale] =
FloatEntry("GVR Render Target Scale", gvrrts_default);
string_entries_[StringID::kResolutionAndroid] =
StringEntry("Resolution (Android)", "Auto");
string_entries_[StringID::kTouchActionControlType] =
StringEntry("Touch Action Control Type", "buttons");
string_entries_[StringID::kTouchMovementControlType] =
StringEntry("Touch Movement Control Type", "swipe");
string_entries_[StringID::kGraphicsQuality] =
StringEntry("Graphics Quality", "Auto");
string_entries_[StringID::kTextureQuality] =
StringEntry("Texture Quality", "Auto");
string_entries_[StringID::kVerticalSync] =
StringEntry("Vertical Sync", "Auto");
string_entries_[StringID::kVRHeadRelativeAudio] =
StringEntry("VR Head Relative Audio", "Auto");
string_entries_[StringID::kMacControllerSubsystem] =
StringEntry("Mac Controller Subsystem", "Classic");
string_entries_[StringID::kTelnetPassword] =
StringEntry("Telnet Password", "changeme");
int_entries_[IntID::kPort] = IntEntry("Port", kDefaultPort);
int_entries_[IntID::kTelnetPort] =
IntEntry("Telnet Port", kDefaultTelnetPort);
bool_entries_[BoolID::kTouchControlsSwipeHidden] =
BoolEntry("Touch Controls Swipe Hidden", false);
bool_entries_[BoolID::kFullscreen] = BoolEntry("Fullscreen", false);
bool_entries_[BoolID::kKickIdlePlayers] =
BoolEntry("Kick Idle Players", false);
bool_entries_[BoolID::kAlwaysUseInternalKeyboard] =
BoolEntry("Always Use Internal Keyboard", false);
bool_entries_[BoolID::kShowFPS] = BoolEntry("Show FPS", false);
bool_entries_[BoolID::kTVBorder] =
BoolEntry("TV Border", g_platform->IsRunningOnTV());
bool_entries_[BoolID::kKeyboardP2Enabled] =
BoolEntry("Keyboard P2 Enabled", false);
bool_entries_[BoolID::kEnablePackageMods] =
BoolEntry("Enable Package Mods", false);
bool_entries_[BoolID::kChatMuted] = BoolEntry("Chat Muted", false);
bool_entries_[BoolID::kEnableRemoteApp] =
BoolEntry("Enable Remote App", true);
bool_entries_[BoolID::kEnableTelnet] = BoolEntry("Enable Telnet", true);
bool_entries_[BoolID::kDisableCameraShake] =
BoolEntry("Disable Camera Shake", false);
bool_entries_[BoolID::kDisableCameraGyro] =
BoolEntry("Disable Camera Gyro", false);
// Now add everything to our name map and make sure all is kosher.
CompleteMap(float_entries_);
CompleteMap(int_entries_);
CompleteMap(string_entries_);
CompleteMap(bool_entries_);
}
auto AppConfig::Resolve(FloatID id) -> float {
auto i = float_entries_.find(id);
if (i == float_entries_.end()) {
throw Exception("Invalid config entry");
}
return i->second.Resolve();
}
auto AppConfig::Resolve(StringID id) -> std::string {
auto i = string_entries_.find(id);
if (i == string_entries_.end()) {
throw Exception("Invalid config entry");
}
return i->second.Resolve();
}
auto AppConfig::Resolve(BoolID id) -> bool {
auto i = bool_entries_.find(id);
if (i == bool_entries_.end()) {
throw Exception("Invalid config entry");
}
return i->second.Resolve();
}
auto AppConfig::Resolve(IntID id) -> int {
auto i = int_entries_.find(id);
if (i == int_entries_.end()) {
throw Exception("Invalid config entry");
}
return i->second.Resolve();
}
} // namespace ballistica

View File

@ -0,0 +1,12 @@
// Released under the MIT License. See LICENSE for details.
#include "ballistica/app/app_globals.h"
namespace ballistica {
AppGlobals::AppGlobals(int argc_in, char** argv_in)
: argc{argc_in},
argv{argv_in},
main_thread_id{std::this_thread::get_id()} {}
} // namespace ballistica

View File

@ -0,0 +1,21 @@
// Released under the MIT License. See LICENSE for details.
#if BA_HEADLESS_BUILD
#include "ballistica/app/headless_app.h"
#include "ballistica/ballistica.h"
namespace ballistica {
// We could technically use the vanilla App class here since we're not
// changing anything.
HeadlessApp::HeadlessApp(Thread* thread) : App(thread) {
// NewThreadTimer(10, true, NewLambdaRunnable([this] {
// assert(g_app);
// g_app->RunEvents();
// }));
}
} // namespace ballistica
#endif // BA_HEADLESS_BUILD

View File

@ -0,0 +1,101 @@
// Released under the MIT License. See LICENSE for details.
#include "ballistica/app/stress_test.h"
#include "ballistica/ballistica.h"
#include "ballistica/graphics/graphics_server.h"
#include "ballistica/graphics/renderer.h"
#include "ballistica/input/input.h"
#include "ballistica/platform/platform.h"
namespace ballistica {
void StressTest::Update() {
assert(InMainThread());
// Handle a little misc stuff here.
// If we're currently running stress-tests, update that stuff.
if (stress_testing_ && g_input) {
// Update our fake inputs to make our dudes run around.
g_input->ProcessStressTesting(stress_test_player_count_);
// Every 10 seconds update our stress-test stats.
millisecs_t t = GetRealTime();
if (t - last_stress_test_update_time_ >= 10000) {
if (stress_test_stats_file_ == nullptr) {
assert(g_platform);
std::string f_name =
g_platform->GetUserPythonDirectory() + "/stress_test_stats.csv";
stress_test_stats_file_ = g_platform->FOpen(f_name.c_str(), "wb");
if (stress_test_stats_file_ != nullptr) {
fprintf(stress_test_stats_file_,
"time,averageFps,nodes,models,collide_models,textures,sounds,"
"pssMem,sharedDirtyMem,privateDirtyMem\n");
fflush(stress_test_stats_file_);
if (g_buildconfig.ostype_android()) {
// On android, let the OS know we've added or removed a file
// (limit to android or we'll get an unimplemented warning).
g_platform->AndroidRefreshFile(f_name);
}
}
}
if (stress_test_stats_file_ != nullptr) {
// See how many frames we've rendered this past interval.
int total_frames_rendered;
if (g_graphics_server && g_graphics_server->renderer()) {
total_frames_rendered =
g_graphics_server->renderer()->total_frames_rendered();
} else {
total_frames_rendered = last_total_frames_rendered_;
}
float avg =
static_cast<float>(total_frames_rendered
- last_total_frames_rendered_)
/ (static_cast<float>(t - last_stress_test_update_time_) / 1000.0f);
last_total_frames_rendered_ = total_frames_rendered;
uint32_t model_count = 0;
uint32_t collide_model_count = 0;
uint32_t texture_count = 0;
uint32_t sound_count = 0;
uint32_t node_count = 0;
if (g_media) {
model_count = g_media->total_model_count();
collide_model_count = g_media->total_collide_model_count();
texture_count = g_media->total_texture_count();
sound_count = g_media->total_sound_count();
}
assert(g_game);
std::string mem_usage = g_platform->GetMemUsageInfo();
fprintf(stress_test_stats_file_, "%d,%.1f,%d,%d,%d,%d,%d,%s\n",
static_cast_check_fit<int>(GetRealTime()), avg, node_count,
model_count, collide_model_count, texture_count, sound_count,
mem_usage.c_str());
fflush(stress_test_stats_file_);
}
last_stress_test_update_time_ = t;
}
}
}
void StressTest::Set(bool enable, int player_count) {
assert(InMainThread());
bool was_stress_testing = stress_testing_;
stress_testing_ = enable;
stress_test_player_count_ = player_count;
// If we're turning on, reset our intervals and things.
if (!was_stress_testing && stress_testing_) {
// So our first sample is 1 interval from now.
last_stress_test_update_time_ = GetRealTime();
// Reset our frames-rendered tally.
if (g_graphics_server && g_graphics_server->renderer()) {
last_total_frames_rendered_ =
g_graphics_server->renderer()->total_frames_rendered();
} else {
// Assume zero if there's no graphics yet.
last_total_frames_rendered_ = 0;
}
}
}
} // namespace ballistica

View File

@ -0,0 +1,110 @@
// Released under the MIT License. See LICENSE for details.
#if BA_VR_BUILD
#include "ballistica/app/vr_app.h"
#include "ballistica/game/game.h"
#include "ballistica/graphics/graphics_server.h"
#include "ballistica/graphics/renderer.h"
namespace ballistica {
VRApp::VRApp(Thread* thread) : App(thread) {}
void VRApp::PushVRSimpleRemoteStateCall(const VRSimpleRemoteState& state) {
PushCall([this, state] {
// Convert this to a full hands state, adding in some simple elbow
// positioning of our own and left/right.
VRHandsState s;
s.l.tx = -0.2f;
s.l.ty = -0.2f;
s.l.tz = -0.3f;
// Hmm; for now lets always assign this as right hand even when its in
// left-handed mode to keep things simple on the back-end. Can change later
// if there's a downside to that.
s.r.type = VRHandType::kDaydreamRemote;
s.r.tx = 0.2f;
s.r.ty = -0.2f;
s.r.tz = -0.3f;
s.r.yaw = state.r0;
s.r.pitch = state.r1;
s.r.roll = state.r2;
VRSetHands(s);
});
}
void VRApp::VRSetDrawDimensions(int w, int h) {
g_graphics_server->VideoResize(w, h);
}
void VRApp::VRPreDraw() {
if (!g_graphics_server || !g_graphics_server->renderer()) {
return;
}
assert(InMainThread());
if (FrameDef* frame_def = g_graphics_server->GetRenderFrameDef()) {
// Note: this could be part of PreprocessRenderFrameDef but
// the non-vr path needs it to be separate since preprocess doesn't
// happen sometimes. Should probably clean that up.
g_graphics_server->RunFrameDefMeshUpdates(frame_def);
// store this for the duration of this frame
vr_render_frame_def_ = frame_def;
g_graphics_server->PreprocessRenderFrameDef(frame_def);
}
}
void VRApp::VRPostDraw() {
assert(InMainThread());
if (!g_graphics_server || !g_graphics_server->renderer()) {
return;
}
if (vr_render_frame_def_) {
g_graphics_server->FinishRenderFrameDef(vr_render_frame_def_);
vr_render_frame_def_ = nullptr;
}
RunRenderUpkeepCycle();
}
void VRApp::VRSetHead(float tx, float ty, float tz, float yaw, float pitch,
float roll) {
assert(InMainThread());
Renderer* renderer = g_graphics_server->renderer();
if (renderer == nullptr) return;
renderer->VRSetHead(tx, ty, tz, yaw, pitch, roll);
}
void VRApp::VRSetHands(const VRHandsState& state) {
assert(InMainThread());
// Pass this along to the renderer (in this same thread) for drawing
// (so hands can be drawn at their absolute most up-to-date positions, etc).
Renderer* renderer = g_graphics_server->renderer();
if (renderer == nullptr) return;
renderer->VRSetHands(state);
// ALSO ship it off to the game/ui thread to actually handle input from it.
g_game->PushVRHandsState(state);
}
void VRApp::VRDrawEye(int eye, float yaw, float pitch, float roll, float tan_l,
float tan_r, float tan_b, float tan_t, float eye_x,
float eye_y, float eye_z, int viewport_x,
int viewport_y) {
if (!g_graphics_server || !g_graphics_server->renderer()) {
return;
}
assert(InMainThread());
if (vr_render_frame_def_) {
// set up VR eye stuff...
Renderer* renderer = g_graphics_server->renderer();
renderer->VRSetEye(eye, yaw, pitch, roll, tan_l, tan_r, tan_b, tan_t, eye_x,
eye_y, eye_z, viewport_x, viewport_y);
g_graphics_server->DrawRenderFrameDef(vr_render_frame_def_);
}
}
} // namespace ballistica
#endif // BA_VR_BUILD

View File

@ -0,0 +1,418 @@
// Released under the MIT License. See LICENSE for details.
#include "ballistica/game/player.h"
#include <algorithm>
#include "ballistica/game/host_activity.h"
#include "ballistica/game/session/host_session.h"
#include "ballistica/generic/utils.h"
#include "ballistica/input/device/joystick.h"
#include "ballistica/python/class/python_class_session_player.h"
#include "ballistica/python/python.h"
#include "ballistica/python/python_context_call.h"
#include "ballistica/scene/node/node_attribute.h"
#include "ballistica/scene/node/node_type.h"
namespace ballistica {
Player::Player(int id_in, HostSession* host_session)
: id_(id_in), creation_time_(GetRealTime()), host_session_(host_session) {
assert(host_session);
assert(InGameThread());
}
Player::~Player() {
assert(InGameThread());
// If we have an input-device attached to us, detach it.
InputDevice* input_device = input_device_.get();
if (input_device) {
input_device->DetachFromPlayer();
}
// Release our ref to ourself if we have one.
if (py_ref_) {
Py_DECREF(py_ref_);
}
}
auto Player::GetName(bool full, bool icon) const -> std::string {
std::string n = full ? full_name_ : name_;
// Quasi-hacky: if they ask for no icon, strip the first char off our string
// if its in the custom-use-range.
if (!icon) {
std::vector<uint32_t> uni = Utils::UnicodeFromUTF8(n, "3f94f4f");
if (!uni.empty() && uni[0] >= 0xE000 && uni[0] <= 0xF8FF) {
uni.erase(uni.begin());
}
return Utils::UTF8FromUnicode(uni);
} else {
return n;
}
}
auto Player::GetHostActivity() const -> HostActivity* {
return host_activity_.get();
}
void Player::SetHostActivity(HostActivity* a) {
assert(InGameThread());
// Make sure we get pulled out of one activity before being added to another.
if (a && in_activity_) {
std::string old_name =
host_activity_.exists()
? PythonRef(host_activity_->GetPyActivity(), PythonRef::kAcquire)
.Str()
: "<nullptr>";
std::string new_name =
PythonRef(a->GetPyActivity(), PythonRef::kAcquire).Str();
BA_LOG_PYTHON_TRACE_ONCE(
"Player::SetHostActivity() called when already in an activity (old="
+ old_name + ", new=" + new_name + ")");
} else if (!a && !in_activity_) {
BA_LOG_PYTHON_TRACE_ONCE(
"Player::SetHostActivity() called with nullptr when not in an "
"activity");
}
host_activity_ = a;
in_activity_ = (a != nullptr);
}
void Player::SetPosition(const Vector3f& position) {
position_ = position;
have_position_ = true;
}
void Player::ResetInput() {
// Hold a ref to ourself while clearing this to make sure
// we don't die midway as a result of freeing something.
Object::Ref<Object> ref(this);
calls_.clear();
left_held_ = right_held_ = up_held_ = down_held_ = have_position_ = false;
}
void Player::SetPyTeam(PyObject* team) {
if (team != nullptr && team != Py_None) {
// We store a weak-ref to this.
py_team_weak_ref_.Steal(PyWeakref_NewRef(team, nullptr));
} else {
py_team_weak_ref_.Release();
}
}
auto Player::GetPyTeam() -> PyObject* {
PyObject* obj = py_team_weak_ref_.get();
if (!obj) {
return Py_None;
}
return PyWeakref_GetObject(obj);
}
void Player::SetPyCharacter(PyObject* character) {
if (character != nullptr && character != Py_None) {
py_character_.Acquire(character);
} else {
py_character_.Release();
}
}
auto Player::GetPyCharacter() -> PyObject* {
return py_character_.exists() ? py_character_.get() : Py_None;
}
void Player::SetPyColor(PyObject* c) { py_color_.Acquire(c); }
auto Player::GetPyColor() -> PyObject* {
return py_color_.exists() ? py_color_.get() : Py_None;
}
void Player::SetPyHighlight(PyObject* c) { py_highlight_.Acquire(c); }
auto Player::GetPyHighlight() -> PyObject* {
return py_highlight_.exists() ? py_highlight_.get() : Py_None;
}
void Player::SetPyActivityPlayer(PyObject* c) { py_activityplayer_.Acquire(c); }
auto Player::GetPyActivityPlayer() -> PyObject* {
return py_activityplayer_.exists() ? py_activityplayer_.get() : Py_None;
}
auto Player::GetPyRef(bool new_ref) -> PyObject* {
assert(InGameThread());
if (py_ref_ == nullptr) {
py_ref_ = PythonClassSessionPlayer::Create(this);
}
if (new_ref) {
Py_INCREF(py_ref_);
}
return py_ref_;
}
void Player::AssignInputCall(InputType type, PyObject* call_obj) {
assert(InGameThread());
assert(static_cast<int>(type) >= 0
&& static_cast<int>(type) < static_cast<int>(InputType::kLast));
// Special case: if they're assigning hold-position-press or
// hold-position-release, or any direction events, we add in a hold-position
// press/release event before we deliver any other events.. that way newly
// created stuff is informed of the hold state and doesn't wrongly think they
// should start moving.
switch (type) {
case InputType::kHoldPositionPress:
case InputType::kHoldPositionRelease:
case InputType::kLeftPress:
case InputType::kLeftRelease:
case InputType::kRightPress:
case InputType::kUpPress:
case InputType::kUpRelease:
case InputType::kDownPress:
case InputType::kDownRelease:
case InputType::kUpDown:
case InputType::kLeftRight: {
send_hold_state_ = true;
break;
}
default:
break;
}
if (call_obj) {
calls_[static_cast<int>(type)] = Object::New<PythonContextCall>(call_obj);
} else {
calls_[static_cast<int>(type)].Clear();
}
// If they assigned l/r, immediately send an update for its current value.
if (type == InputType::kLeftRight) {
RunInput(type, lr_state_);
}
// Same for up/down.
if (type == InputType::kUpDown) {
RunInput(type, ud_state_);
}
// Same for run.
if (type == InputType::kRun) {
RunInput(type, run_state_);
}
// Same for fly.
if (type == InputType::kFlyPress && fly_held_) {
RunInput(type);
}
}
void Player::RunInput(InputType type, float value) {
assert(InGameThread());
const float threshold = kJoystickDiscreteThresholdFloat;
// Most input commands cause us to reset the player's time-out
// there are a few exceptions though - very small analog values
// get ignored since they can come through without user intervention.
bool reset_time_out = true;
if (type == InputType::kLeftRight || type == InputType::kUpDown) {
if (std::abs(value) < 0.3f) {
reset_time_out = false;
}
}
if (type == InputType::kRun) {
if (value < 0.3f) {
reset_time_out = false;
}
}
// Also ignore hold-position stuff since it can come through without user
// interaction.
if ((type == InputType::kHoldPositionPress)
|| (type == InputType::kHoldPositionRelease))
reset_time_out = false;
if (reset_time_out) {
time_out_ = BA_PLAYER_TIME_OUT;
}
// Keep track of the hold-position state that comes through here.
// any-time hold position buttons are re-assigned, we subsequently
// re-send the current hold-state so whatever its driving starts out correctly
// held if need be.
if (type == InputType::kHoldPositionPress) {
hold_position_ = true;
} else if (type == InputType::kHoldPositionRelease) {
hold_position_ = false;
} else if (type == InputType::kFlyPress) {
fly_held_ = true;
} else if (type == InputType::kFlyRelease) {
fly_held_ = false;
}
// If we were supposed to deliver hold-state, go ahead and do that first.
if (send_hold_state_) {
send_hold_state_ = false;
if (hold_position_) {
RunInput(InputType::kHoldPositionPress);
} else {
RunInput(InputType::kHoldPositionRelease);
}
}
// Let's make our life simpler by converting held-position-joystick-events..
{
// We need to store these since we might look at them during a hold-position
// event when we don't have their originating events available.
if (type == InputType::kLeftRight) {
lr_state_ = value;
}
if (type == InputType::kUpDown) {
ud_state_ = value;
}
if (type == InputType::kRun) {
run_state_ = value;
}
// Special input commands - keep track of left/right and up/down positions
// so we can deliver simple "leftUp", "leftDown", etc type of events
// in addition to the standard absolute leftRight positions, etc.
if (type == InputType::kLeftRight || type == InputType::kHoldPositionPress
|| type == InputType::kHoldPositionRelease) {
float arg = lr_state_;
if (hold_position_) {
arg = 0.0f; // Throttle is off.
}
if (left_held_) {
if (arg > -threshold) {
left_held_ = false;
RunInput(InputType::kLeftRelease);
}
} else if (right_held_) {
if (arg < threshold) {
right_held_ = false;
RunInput(InputType::kRightRelease);
}
} else {
if (arg >= threshold) {
if (!left_held_ && !up_held_ && !down_held_) {
right_held_ = true;
RunInput(InputType::kRightPress);
}
} else if (arg <= -threshold) {
if (!right_held_ && !up_held_ && !down_held_) {
left_held_ = true;
RunInput(InputType::kLeftPress);
}
}
}
}
if (type == InputType::kUpDown || type == InputType::kHoldPositionPress
|| type == InputType::kHoldPositionRelease) {
float arg = ud_state_;
if (hold_position_) arg = 0.0f; // throttle is off;
if (up_held_) {
if (arg < threshold) {
up_held_ = false;
RunInput(InputType::kUpRelease);
}
} else if (down_held_) {
if (arg > -threshold) {
down_held_ = false;
RunInput(InputType::kDownRelease);
}
} else {
if (arg <= -threshold) {
if (!left_held_ && !right_held_ && !up_held_) {
down_held_ = true;
RunInput(InputType::kDownPress);
}
} else if (arg >= threshold) {
if (!left_held_ && !up_held_ && !right_held_) {
up_held_ = true;
RunInput(InputType::kUpPress);
}
}
}
}
}
auto j = calls_.find(static_cast<int>(type));
if (j != calls_.end() && j->second.exists()) {
if (type == InputType::kRun) {
PythonRef args(
Py_BuildValue("(f)", std::min(1.0f, std::max(0.0f, value))),
PythonRef::kSteal);
j->second->Run(args.get());
} else if (type == InputType::kLeftRight || type == InputType::kUpDown) {
PythonRef args(
Py_BuildValue("(f)", std::min(1.0f, std::max(-1.0f, value))),
PythonRef::kSteal);
j->second->Run(args.get());
} else {
j->second->Run();
}
}
}
auto Player::GetHostSession() const -> HostSession* {
return host_session_.get();
}
void Player::SetName(const std::string& name, const std::string& full_name,
bool is_real) {
assert(InGameThread());
HostSession* host_session = GetHostSession();
BA_PRECONDITION(host_session);
name_is_real_ = is_real;
name_ = host_session->GetUnusedPlayerName(this, name);
full_name_ = full_name;
// If we're already in the game and our name is changing, we need to update
// the roster.
if (accepted_) {
g_game->UpdateGameRoster();
}
}
void Player::InputCommand(InputType type, float value) {
assert(InGameThread());
switch (type) {
case InputType::kUpDown:
case InputType::kLeftRight:
case InputType::kRun:
RunInput(type, value);
break;
// case InputType::kReset:
// Log("Error: FIXME: player-input-reset command unimplemented");
// break;
default:
RunInput(type);
break;
}
}
void Player::SetInputDevice(InputDevice* input_device) {
input_device_ = input_device;
}
auto Player::GetPublicAccountID() const -> std::string {
assert(InGameThread());
if (input_device_.exists()) {
return input_device_->GetPublicAccountID();
}
return "";
}
void Player::SetIcon(const std::string& tex_name,
const std::string& tint_tex_name,
const std::vector<float>& tint_color,
const std::vector<float>& tint2_color) {
assert(tint_color.size() == 3);
assert(tint2_color.size() == 3);
icon_tex_name_ = tex_name;
icon_tint_tex_name_ = tint_tex_name;
icon_tint_color_ = tint_color;
icon_tint2_color_ = tint2_color;
icon_set_ = true;
}
} // namespace ballistica

View File

@ -0,0 +1,109 @@
// Released under the MIT License. See LICENSE for details.
#include "ballistica/game/player_spec.h"
#include <cstdlib>
#include "ballistica/app/app_globals.h"
#include "ballistica/game/account.h"
#include "ballistica/generic/json.h"
#include "ballistica/generic/utils.h"
#include "ballistica/platform/platform.h"
namespace ballistica {
PlayerSpec::PlayerSpec() : account_type_(AccountType::kInvalid) {}
PlayerSpec::PlayerSpec(const std::string& s) {
cJSON* root_obj = cJSON_Parse(s.c_str());
bool success = false;
if (root_obj) {
cJSON* name_obj = cJSON_GetObjectItem(root_obj, "n");
cJSON* short_name_obj = cJSON_GetObjectItem(root_obj, "sn");
cJSON* account_obj = cJSON_GetObjectItem(root_obj, "a");
if (name_obj && short_name_obj && account_obj) {
name_ = Utils::GetValidUTF8(name_obj->valuestring, "psps");
short_name_ = Utils::GetValidUTF8(short_name_obj->valuestring, "psps2");
// Account type may technically be something we don't recognize,
// but that's ok.. it'll just be 'invalid' to us in that case
account_type_ = Account::AccountTypeFromString(account_obj->valuestring);
success = true;
}
cJSON_Delete(root_obj);
}
if (!success) {
Log("Error creating PlayerSpec from string: '" + s + "'");
name_ = "<error>";
short_name_ = "";
account_type_ = AccountType::kInvalid;
}
}
auto PlayerSpec::GetDisplayString() const -> std::string {
return Account::AccountTypeToIconString(account_type_) + name_;
}
auto PlayerSpec::GetShortName() const -> std::string {
if (short_name_.empty()) {
return name_;
}
return short_name_;
}
auto PlayerSpec::operator==(const PlayerSpec& spec) const -> bool {
// NOTE: need to add account ID in here once that's available
return (spec.name_ == name_ && spec.short_name_ == short_name_
&& spec.account_type_ == account_type_);
}
auto PlayerSpec::GetSpecString() const -> std::string {
cJSON* root;
root = cJSON_CreateObject();
cJSON_AddStringToObject(root, "n", name_.c_str());
cJSON_AddStringToObject(root, "a",
Account::AccountTypeToString(account_type_).c_str());
cJSON_AddStringToObject(root, "sn", short_name_.c_str());
char* out = cJSON_PrintUnformatted(root);
std::string out_s = out;
free(out);
cJSON_Delete(root);
// We should never allow ourself to have all this add up to more than 256.
assert(out_s.size() < 256);
return out_s;
}
auto PlayerSpec::GetAccountPlayerSpec() -> PlayerSpec {
PlayerSpec spec;
if (g_account->GetAccountState() == AccountState::kSignedIn) {
spec.account_type_ = g_app_globals->account_type;
spec.name_ =
Utils::GetValidUTF8(g_account->GetAccountName().c_str(), "bsgaps");
} else {
spec.name_ =
Utils::GetValidUTF8(g_platform->GetDeviceName().c_str(), "bsgaps2");
}
if (spec.name_.size() > 100) {
// FIXME should perhaps clamp this in unicode space
Log("account name size too long: '" + spec.name_ + "'");
spec.name_.resize(100);
spec.name_ = Utils::GetValidUTF8(spec.name_.c_str(), "bsgaps3");
}
return spec;
}
auto PlayerSpec::GetDummyPlayerSpec(const std::string& name) -> PlayerSpec {
PlayerSpec spec;
spec.name_ = Utils::GetValidUTF8(name.c_str(), "bsgdps1");
if (spec.name_.size() > 100) {
// FIXME should perhaps clamp this in unicode space
Log("dummy player spec name too long: '" + spec.name_ + "'");
spec.name_.resize(100);
spec.name_ = Utils::GetValidUTF8(spec.name_.c_str(), "bsgdps2");
}
return spec;
}
} // namespace ballistica

View File

@ -0,0 +1,56 @@
// Released under the MIT License. See LICENSE for details.
#include "ballistica/networking/sockaddr.h"
namespace ballistica {
SockAddr::SockAddr(const std::string& addr, int port) {
memset(&addr_, 0, sizeof(addr_));
// try ipv4...
{
// inet_pton is not available on XP :-/
// hmmm at this point we probably don't care; should test inet_pton.
// #if BA_OSTYPE_WINDOWS
// int addr_size = sizeof(addr_);
// std::wstring addr2;
// addr2.assign(addr.begin(), addr.end());
// struct sockaddr_in* a4 = reinterpret_cast<sockaddr_in*>(&addr_);
// struct sockaddr_in6* a6 = reinterpret_cast<sockaddr_in6*>(&addr_);
// int result =
// WSAStringToAddress(const_cast<wchar_t*>(addr2.c_str()), AF_INET,
// nullptr, (LPSOCKADDR)a4, &addr_size);
// if (result == 0) {
// if (a4->sin_family == AF_INET) {
// a4->sin_port = htons(port);
// return;
// } else if (a6->sin6_family == AF_INET6) {
// a6->sin6_port = htons(port);
// }
// }
// #else
struct in_addr addr_out {};
int result = inet_pton(AF_INET, addr.c_str(), &addr_out);
if (result == 1) {
auto* a = reinterpret_cast<sockaddr_in*>(&addr_);
a->sin_family = AF_INET;
a->sin_port = htons(port); // NOLINT
a->sin_addr = addr_out;
return;
} else {
struct in6_addr addr6Out {};
result = inet_pton(AF_INET6, addr.c_str(), &addr6Out);
if (result == 1) {
auto* a = reinterpret_cast<sockaddr_in6*>(&addr_);
a->sin6_family = AF_INET6;
a->sin6_port = htons(port); // NOLINT
a->sin6_addr = addr6Out;
return;
}
}
// #endif
}
throw Exception("Invalid address: '" + addr + "'");
}
} // namespace ballistica