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
|
### 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
@@ -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
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user