Rename plugin and add player position logging
Replace CommonLibF4 template identity with Fallout4Together across README, setup and xmake; update developer log with build/test entries. Implement player transform tracking in src: add PlayerTransform struct, change detection (position + wrapped Z rotation), throttling (minimum interval), and logging to Fallout4Together.log. Wire up F4SE messaging listener and a permanent task to poll on the game thread; include <chrono> and update startup log message. Add warnings for missing task/messaging interfaces and when player reference is unavailable.
This commit is contained in:
+118
@@ -89,3 +89,121 @@ Use this format for future updates:
|
||||
|
||||
-
|
||||
```
|
||||
## 2026-05-31
|
||||
|
||||
### What Changed
|
||||
|
||||
- Built the initial Fallout 4 Together DLL.
|
||||
- Installed the DLL into `Data/F4SE/Plugins/`.
|
||||
- Launched Fallout 4 through F4SE.
|
||||
|
||||
### What Worked
|
||||
|
||||
- The plugin loaded successfully.
|
||||
- The plugin wrote a log file.
|
||||
- The template test message `Hello World!` appeared in the log.
|
||||
|
||||
### What Broke
|
||||
|
||||
- The plugin is still using the template log name and identity.
|
||||
|
||||
### Notes
|
||||
|
||||
- The current log file is named `commonlibf4-template.log`.
|
||||
- The next step is to rename the plugin identity and log output to `Fallout4Together`.
|
||||
|
||||
### Next Steps
|
||||
|
||||
- Replace template name references with `Fallout4Together`.
|
||||
- Replace `Hello World!` with a Fallout 4 Together startup message.
|
||||
- Rebuild the DLL.
|
||||
- Launch through F4SE again.
|
||||
- Confirm `Fallout4Together.log` is created.
|
||||
|
||||
|
||||
## 2026-05-31
|
||||
|
||||
### What Changed
|
||||
|
||||
- Renamed the plugin/log identity from the CommonLibF4 template to Fallout4Together.
|
||||
- Rebuilt and installed `Fallout4Together.dll`.
|
||||
- Launched Fallout 4 through F4SE.
|
||||
|
||||
### What Worked
|
||||
|
||||
- `Fallout4Together.log` was created.
|
||||
- The plugin loaded successfully.
|
||||
- The plugin startup code executed.
|
||||
|
||||
### What Broke
|
||||
|
||||
- Nothing currently recorded.
|
||||
|
||||
### Notes
|
||||
|
||||
- The log still contains the template message `Hello World!`.
|
||||
- Next step is to replace this message with a Fallout 4 Together startup message.
|
||||
|
||||
### Next Steps
|
||||
|
||||
- Replace `Hello World!` with a proper plugin startup log message.
|
||||
- Rebuild and reinstall the DLL.
|
||||
- Confirm the updated message appears in `Fallout4Together.log`.
|
||||
- Begin testing local player position readout.
|
||||
|
||||
## 2026-05-31
|
||||
|
||||
### What Changed
|
||||
|
||||
- Updated the Fallout 4 Together plugin startup message.
|
||||
- Added local player position readout.
|
||||
- Logged the player's X, Y, Z position and Z rotation angle.
|
||||
|
||||
### What Worked
|
||||
|
||||
- `Fallout4Together.dll` built successfully.
|
||||
- Fallout 4 launched through F4SE.
|
||||
- `Fallout4Together.log` was created.
|
||||
- The plugin loaded successfully.
|
||||
- The plugin safely read and logged the local player position.
|
||||
|
||||
### What Broke
|
||||
|
||||
- Nothing recorded.
|
||||
|
||||
### Notes
|
||||
|
||||
- The first successful player position log was:
|
||||
|
||||
```text
|
||||
Player position: X=2048.00, Y=2048.00, Z=0.00, AngleZ=0.00
|
||||
|
||||
## 2026-05-31
|
||||
|
||||
### What Changed
|
||||
|
||||
- Updated player position logging so it only writes when the player position changes.
|
||||
- Added throttling so movement updates do not flood the log.
|
||||
- Rebuilt and tested `Fallout4Together.dll`.
|
||||
|
||||
### What Worked
|
||||
|
||||
- Fallout 4 launched through F4SE.
|
||||
- `Fallout4Together.log` was created.
|
||||
- The plugin logged the player position while moving.
|
||||
- Position logging now updates at a readable pace instead of many times per second.
|
||||
|
||||
### What Broke
|
||||
|
||||
- Nothing recorded.
|
||||
|
||||
### Notes
|
||||
|
||||
- The player transform readout milestone is now working.
|
||||
- The plugin can access the local player and track movement changes.
|
||||
|
||||
### Next Steps
|
||||
|
||||
- Create a local test server.
|
||||
- Send player transform data from the plugin to the server.
|
||||
- Keep the first network test local-only.
|
||||
+4
-4
@@ -1,6 +1,6 @@
|
||||
# CommonLibF4 Plugin Template
|
||||
# Fallout4Together Plugin
|
||||
|
||||
This is a basic plugin template using CommonLibF4.
|
||||
This is the native Fallout4Together plugin using CommonLibF4.
|
||||
|
||||
### Requirements
|
||||
* [XMake](https://xmake.io) [3.0.0+]
|
||||
@@ -8,8 +8,8 @@ This is a basic plugin template using CommonLibF4.
|
||||
|
||||
## Getting Started
|
||||
```bat
|
||||
git clone --recurse-submodules https://github.com/libxse/commonlibf4-template
|
||||
cd commonlibf4-template
|
||||
git clone --recurse-submodules https://github.com/libxse/Fallout4Together
|
||||
cd Fallout4Together
|
||||
```
|
||||
|
||||
### Build
|
||||
|
||||
+3
-3
@@ -1,12 +1,12 @@
|
||||
# Plugin Setup
|
||||
|
||||
The Fallout 4 Together native plugin is based on `libxse/commonlibf4-template`.
|
||||
The Fallout 4 Together native plugin is based on `Fallout4Together`.
|
||||
|
||||
## Selected Template
|
||||
|
||||
```text
|
||||
Template: libxse/commonlibf4-template
|
||||
Template URL: https://github.com/libxse/commonlibf4-template
|
||||
Template: Fallout4Together
|
||||
Template URL: https://github.com/libxse/Fallout4Together
|
||||
Use: Copied into plugin/ as the starting plugin scaffold
|
||||
Forked: No
|
||||
```
|
||||
|
||||
+151
-1
@@ -1,8 +1,158 @@
|
||||
namespace
|
||||
{
|
||||
struct PlayerTransform
|
||||
{
|
||||
float x;
|
||||
float y;
|
||||
float z;
|
||||
float angleZ;
|
||||
};
|
||||
|
||||
constexpr auto kPositionLogThreshold = 10.0F;
|
||||
constexpr auto kRotationLogThresholdDegrees = 1.0F;
|
||||
constexpr auto kRotationLogThresholdRadians = kRotationLogThresholdDegrees * 3.14159265358979323846F / 180.0F;
|
||||
constexpr auto kMinimumLogInterval = 1s;
|
||||
|
||||
RE::PlayerCharacter* TryGetLocalPlayer()
|
||||
{
|
||||
return RE::PlayerCharacter::GetSingleton();
|
||||
}
|
||||
|
||||
PlayerTransform GetPlayerTransform(const RE::PlayerCharacter& a_player)
|
||||
{
|
||||
const auto position = a_player.GetPosition();
|
||||
|
||||
return {
|
||||
position.x,
|
||||
position.y,
|
||||
position.z,
|
||||
a_player.data.angle.z
|
||||
};
|
||||
}
|
||||
|
||||
bool HasTransformChanged(const PlayerTransform& a_previous, const PlayerTransform& a_current)
|
||||
{
|
||||
const auto deltaX = a_current.x - a_previous.x;
|
||||
const auto deltaY = a_current.y - a_previous.y;
|
||||
const auto deltaZ = a_current.z - a_previous.z;
|
||||
auto deltaAngleZ = a_current.angleZ - a_previous.angleZ;
|
||||
|
||||
constexpr auto fullRotationRadians = 360.0F * 3.14159265358979323846F / 180.0F;
|
||||
constexpr auto halfRotationRadians = fullRotationRadians / 2.0F;
|
||||
while (deltaAngleZ > halfRotationRadians) {
|
||||
deltaAngleZ -= fullRotationRadians;
|
||||
}
|
||||
while (deltaAngleZ < -halfRotationRadians) {
|
||||
deltaAngleZ += fullRotationRadians;
|
||||
}
|
||||
|
||||
const auto squaredPositionDelta = (deltaX * deltaX) + (deltaY * deltaY) + (deltaZ * deltaZ);
|
||||
constexpr auto squaredPositionThreshold = kPositionLogThreshold * kPositionLogThreshold;
|
||||
|
||||
return squaredPositionDelta > squaredPositionThreshold ||
|
||||
deltaAngleZ > kRotationLogThresholdRadians || deltaAngleZ < -kRotationLogThresholdRadians;
|
||||
}
|
||||
|
||||
void LogPlayerTransform(const PlayerTransform& a_transform)
|
||||
{
|
||||
REX::INFO("Player position: X={:.2f}, Y={:.2f}, Z={:.2f}, AngleZ={:.2f}",
|
||||
a_transform.x,
|
||||
a_transform.y,
|
||||
a_transform.z,
|
||||
a_transform.angleZ);
|
||||
}
|
||||
|
||||
void CheckAndLogPlayerPositionChange()
|
||||
{
|
||||
const auto* player = TryGetLocalPlayer();
|
||||
if (!player) {
|
||||
static bool playerUnavailableWarningLogged = false;
|
||||
if (!playerUnavailableWarningLogged) {
|
||||
REX::WARN("Player position unavailable: local player reference is not available yet.");
|
||||
playerUnavailableWarningLogged = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
static bool hasLastTransform = false;
|
||||
static PlayerTransform lastTransform{};
|
||||
static auto lastLogTime = std::chrono::steady_clock::now();
|
||||
|
||||
const auto currentTransform = GetPlayerTransform(*player);
|
||||
if (!hasLastTransform) {
|
||||
lastTransform = currentTransform;
|
||||
hasLastTransform = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!HasTransformChanged(lastTransform, currentTransform)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto currentTime = std::chrono::steady_clock::now();
|
||||
if (currentTime - lastLogTime < kMinimumLogInterval) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Player transforms can change by tiny amounts every frame. Throttling keeps the
|
||||
// readout useful for debugging movement without flooding Fallout4Together.log.
|
||||
LogPlayerTransform(currentTransform);
|
||||
lastTransform = currentTransform;
|
||||
lastLogTime = currentTime;
|
||||
}
|
||||
|
||||
void StartPlayerPositionPolling()
|
||||
{
|
||||
static bool pollingStarted = false;
|
||||
if (pollingStarted) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto* taskInterface = F4SE::GetTaskInterface();
|
||||
if (!taskInterface) {
|
||||
REX::WARN("Failed to get F4SE task interface; player position changes will not be logged.");
|
||||
return;
|
||||
}
|
||||
|
||||
// The player reference may not exist at plugin load time. A permanent task lets us
|
||||
// check on the game thread after load, while the logging gate keeps updates readable.
|
||||
taskInterface->AddTaskPermanent([]() {
|
||||
CheckAndLogPlayerPositionChange();
|
||||
});
|
||||
|
||||
pollingStarted = true;
|
||||
}
|
||||
|
||||
void F4SEAPI OnF4SEMessage(F4SE::MessagingInterface::Message* a_msg)
|
||||
{
|
||||
if (!a_msg) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (a_msg->type) {
|
||||
case F4SE::MessagingInterface::kNewGame:
|
||||
case F4SE::MessagingInterface::kPostLoadGame:
|
||||
case F4SE::MessagingInterface::kGameLoaded:
|
||||
// Plugin load happens before a save/new game has finished creating the player.
|
||||
// These messages are the minimal safe timing point to start polling for movement.
|
||||
StartPlayerPositionPolling();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
F4SE_PLUGIN_LOAD(const F4SE::LoadInterface* a_f4se)
|
||||
{
|
||||
F4SE::Init(a_f4se);
|
||||
|
||||
REX::INFO("Hello World!");
|
||||
REX::INFO("Fallout 4 Together plugin loaded successfully.");
|
||||
|
||||
const auto* messaging = F4SE::GetMessagingInterface();
|
||||
if (!messaging || !messaging->RegisterListener(OnF4SEMessage)) {
|
||||
REX::WARN("Failed to register F4SE message listener; player position will not be logged.");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
|
||||
#include <RE/Fallout.h>
|
||||
#include <F4SE/F4SE.h>
|
||||
|
||||
|
||||
+3
-3
@@ -15,9 +15,9 @@ add_rules("plugin.vsxmake.autoupdate")
|
||||
-- define targets
|
||||
target("Fallout4Together")
|
||||
add_rules("commonlibf4.plugin", {
|
||||
name = "commonlibf4-template",
|
||||
author = "libxse",
|
||||
description = "F4SE plugin template using CommonLibF4"
|
||||
name = "Fallout4Together",
|
||||
author = "Fallout4Together",
|
||||
description = "Fallout4Together F4SE plugin using CommonLibF4"
|
||||
})
|
||||
|
||||
-- add src files
|
||||
|
||||
Reference in New Issue
Block a user