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:
2026-05-31 17:55:27 +12:00
parent b61043a3fe
commit 88506e0415
6 changed files with 281 additions and 11 deletions
+118
View File
@@ -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
View File
@@ -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 ### Requirements
* [XMake](https://xmake.io) [3.0.0+] * [XMake](https://xmake.io) [3.0.0+]
@@ -8,8 +8,8 @@ This is a basic plugin template using CommonLibF4.
## Getting Started ## Getting Started
```bat ```bat
git clone --recurse-submodules https://github.com/libxse/commonlibf4-template git clone --recurse-submodules https://github.com/libxse/Fallout4Together
cd commonlibf4-template cd Fallout4Together
``` ```
### Build ### Build
+3 -3
View File
@@ -1,12 +1,12 @@
# Plugin Setup # 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 ## Selected Template
```text ```text
Template: libxse/commonlibf4-template Template: Fallout4Together
Template URL: https://github.com/libxse/commonlibf4-template Template URL: https://github.com/libxse/Fallout4Together
Use: Copied into plugin/ as the starting plugin scaffold Use: Copied into plugin/ as the starting plugin scaffold
Forked: No Forked: No
``` ```
+151 -1
View File
@@ -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_PLUGIN_LOAD(const F4SE::LoadInterface* a_f4se)
{ {
F4SE::Init(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; return true;
} }
+2
View File
@@ -1,5 +1,7 @@
#pragma once #pragma once
#include <chrono>
#include <RE/Fallout.h> #include <RE/Fallout.h>
#include <F4SE/F4SE.h> #include <F4SE/F4SE.h>
+3 -3
View File
@@ -15,9 +15,9 @@ add_rules("plugin.vsxmake.autoupdate")
-- define targets -- define targets
target("Fallout4Together") target("Fallout4Together")
add_rules("commonlibf4.plugin", { add_rules("commonlibf4.plugin", {
name = "commonlibf4-template", name = "Fallout4Together",
author = "libxse", author = "Fallout4Together",
description = "F4SE plugin template using CommonLibF4" description = "Fallout4Together F4SE plugin using CommonLibF4"
}) })
-- add src files -- add src files