More work on tangibleCharacter, especially thread synch stuff
This commit is contained in:
Binary file not shown.
@@ -19,7 +19,6 @@ AIntegrationGameModeBase::AIntegrationGameModeBase()
|
|||||||
TangibleManager = NewObject<UlxTangibleManager>();
|
TangibleManager = NewObject<UlxTangibleManager>();
|
||||||
PlayerId = 0;
|
PlayerId = 0;
|
||||||
EngineSeconds = 0.0;
|
EngineSeconds = 0.0;
|
||||||
NextThreadTrigger = 1.0;
|
|
||||||
//PrimaryActorTick.bCanEverTick = true; // Probably wrong
|
//PrimaryActorTick.bCanEverTick = true; // Probably wrong
|
||||||
//PrimaryActorTick.bTickEvenWhenPaused = true; // Probably wrong
|
//PrimaryActorTick.bTickEvenWhenPaused = true; // Probably wrong
|
||||||
//PrimaryActorTick.TickGroup = TG_PrePhysics; // Probably wrong
|
//PrimaryActorTick.TickGroup = TG_PrePhysics; // Probably wrong
|
||||||
@@ -27,11 +26,15 @@ AIntegrationGameModeBase::AIntegrationGameModeBase()
|
|||||||
SetActorTickInterval(0.0f);
|
SetActorTickInterval(0.0f);
|
||||||
DebugPrintControl::EnableCollection();
|
DebugPrintControl::EnableCollection();
|
||||||
ResetToInitialState();
|
ResetToInitialState();
|
||||||
|
OnWorldPreActorTickHandle = FWorldDelegates::OnWorldPreActorTick.AddUObject(this, &AIntegrationGameModeBase::OnWorldPreActorTick);
|
||||||
|
OnWorldPostActorTickHandle = FWorldDelegates::OnWorldPostActorTick.AddUObject(this, &AIntegrationGameModeBase::OnWorldPostActorTick);
|
||||||
}
|
}
|
||||||
|
|
||||||
AIntegrationGameModeBase::~AIntegrationGameModeBase()
|
AIntegrationGameModeBase::~AIntegrationGameModeBase()
|
||||||
{
|
{
|
||||||
ResetToInitialState();
|
ResetToInitialState();
|
||||||
|
FWorldDelegates::OnWorldPreActorTick.Remove(OnWorldPreActorTickHandle);
|
||||||
|
FWorldDelegates::OnWorldPostActorTick.Remove(OnWorldPostActorTickHandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
// This method runs in the background thread,
|
// This method runs in the background thread,
|
||||||
@@ -76,7 +79,6 @@ void AIntegrationGameModeBase::ResetToInitialState()
|
|||||||
|
|
||||||
// Reset the clocks.
|
// Reset the clocks.
|
||||||
EngineSeconds = 0;
|
EngineSeconds = 0;
|
||||||
NextThreadTrigger = 1.0;
|
|
||||||
NextRotateCube = 1.0;
|
NextRotateCube = 1.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,41 +103,53 @@ void AIntegrationGameModeBase::UpdateConsoleOutput() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AIntegrationGameModeBase::MaybeTriggerUpdateTask(float deltaseconds) {
|
|
||||||
if (!Playing) return;
|
|
||||||
FlxLockedWrapper lockedwrap(LockableWrapper);
|
|
||||||
if (EngineSeconds >= NextThreadTrigger)
|
|
||||||
{
|
|
||||||
LuprexUpdateTask.Trigger();
|
|
||||||
NextThreadTrigger += 0.05;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#pragma optimize("", off)
|
#pragma optimize("", off)
|
||||||
void AIntegrationGameModeBase::UpdateTangibles() {
|
void AIntegrationGameModeBase::UpdateTangibles() {
|
||||||
double radius = 1000.0; // Hardwired for now.
|
double radius = 1000.0; // Hardwired for now.
|
||||||
using TanArray = UlxTangibleManager::TanArray;
|
using TanArray = UlxTangibleManager::TanArray;
|
||||||
if (!Playing) return;
|
if (!Playing) return;
|
||||||
FlxLockedWrapper w(LockableWrapper);
|
TanArray alltans;
|
||||||
PlayerId = w.GetActor();
|
{
|
||||||
IdView nearids = w.GetNear(PlayerId, radius, radius, radius);
|
FlxLockedWrapper w(LockableWrapper);
|
||||||
TangibleManager->UpdateNearAccordingToLuprex(nearids);
|
PlayerId = w.GetActor();
|
||||||
TanArray alltans = TangibleManager->GetAllTangibles();
|
IdView nearids = w.GetNear(PlayerId, radius, radius, radius);
|
||||||
IdArray allids = TangibleManager->GetIds(alltans);
|
TangibleManager->UpdateNearAccordingToLuprex(nearids);
|
||||||
StringViewVec allqueues = w.GetAnimationQueues(allids);
|
alltans = TangibleManager->GetAllTangibles();
|
||||||
|
IdArray allids = TangibleManager->GetIds(alltans);
|
||||||
|
StringViewVec allqueues = w.GetAnimationQueues(allids);
|
||||||
|
for (int i = 0; i < alltans.Num(); i++) {
|
||||||
|
alltans[i]->UpdateAnimationQueue(allqueues[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// This is where we run the blueprint code that updates animation
|
||||||
|
// states. Be aware that the blueprint code could call back
|
||||||
|
// into the gamemode, which could in turn access the FlxLockedWrapper.
|
||||||
|
// This is why we've released the FlxLockedWrapper before doing this.
|
||||||
for (int i = 0; i < alltans.Num(); i++) {
|
for (int i = 0; i < alltans.Num(); i++) {
|
||||||
alltans[i]->UpdateAnimationQueue(allqueues[i]);
|
alltans[i]->MaybeExecuteAnimStateChanged();
|
||||||
}
|
}
|
||||||
TangibleManager->RecalcNearAccordingToUnreal(PlayerId, radius);
|
TangibleManager->RecalcNearAccordingToUnreal(PlayerId, radius);
|
||||||
TangibleManager->DeleteFarawayTangibles();
|
TangibleManager->DeleteFarawayTangibles();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AIntegrationGameModeBase::ExecuteDebuggingCommand(const FString &fs) {
|
void AIntegrationGameModeBase::InvokeEngioMove(const FString &action, const FVector &xyz) {
|
||||||
|
FTCHARToUTF8 utf8action(action);
|
||||||
|
std::string uaction(utf8action.Get(), utf8action.Length());
|
||||||
|
FlxStreamBuffer sb;
|
||||||
|
sb.write_string("move"); // Function name within engio
|
||||||
|
sb.write_simple_dynamic_tag(SimpleDynamicTag::STRING);
|
||||||
|
sb.write_string(uaction);
|
||||||
|
sb.write_simple_dynamic_tag(SimpleDynamicTag::VECTOR);
|
||||||
|
sb.write_fvector(xyz);
|
||||||
|
std::string_view datapk = sb.view();
|
||||||
|
FlxLockedWrapper w(LockableWrapper);
|
||||||
|
int64 player = w.GetActor();
|
||||||
|
w->play_invoke_engio(w.Get(), player, datapk.size(), datapk.data());
|
||||||
|
}
|
||||||
|
|
||||||
|
void AIntegrationGameModeBase::ExecuteDebuggingCommand(FlxLockedWrapper &w, const FString &fs) {
|
||||||
if (fs == "\\invokeplayer") {
|
if (fs == "\\invokeplayer") {
|
||||||
DPrint(TEXT("Trying to invoke 'myfunction' in lua"));
|
DPrint(TEXT("Trying to invoke 'myfunction' in lua"));
|
||||||
FlxLockedWrapper w(LockableWrapper);
|
|
||||||
FlxStreamBuffer sb;
|
FlxStreamBuffer sb;
|
||||||
sb.write_string("myfunction");
|
sb.write_string("myfunction");
|
||||||
sb.write_simple_dynamic_tag(SimpleDynamicTag::NUMBER);
|
sb.write_simple_dynamic_tag(SimpleDynamicTag::NUMBER);
|
||||||
@@ -167,31 +181,39 @@ void AIntegrationGameModeBase::ConsoleSendInput(const FString& fs)
|
|||||||
// lua code contains '\\', we'll catch it instead of passing it
|
// lua code contains '\\', we'll catch it instead of passing it
|
||||||
// through. There's no simple solution, though.
|
// through. There's no simple solution, though.
|
||||||
if (fs[0] == '\\') {
|
if (fs[0] == '\\') {
|
||||||
ExecuteDebuggingCommand(fs);
|
ExecuteDebuggingCommand(w, fs);
|
||||||
} else {
|
} else {
|
||||||
const TCHAR* fstchar = *fs;
|
FTCHARToUTF8 utf8fs(fs);
|
||||||
if (sizeof(TCHAR) == 2)
|
std::string ufs(utf8fs.Get(), utf8fs.Length());
|
||||||
{
|
ufs = ufs + "\n";
|
||||||
std::u16string_view fsview((const char16_t*)fstchar, fs.Len());
|
w->play_recv_incoming(w.Get(), 0, ufs.size(), ufs.c_str());
|
||||||
std::string utf8 = drvutil::utf16_to_utf8(fsview);
|
|
||||||
utf8 = utf8 + "\n";
|
|
||||||
w->play_recv_incoming(w.Get(), 0, utf8.size(), utf8.c_str());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AIntegrationGameModeBase::OnWorldPreActorTick(UWorld* InWorld, ELevelTick InLevelTick, float deltaseconds)
|
||||||
|
{
|
||||||
|
if(Playing && (GetWorld() == InWorld) && (InLevelTick == LEVELTICK_All))
|
||||||
|
{
|
||||||
|
LuprexUpdateTask.Wait();
|
||||||
|
EngineSeconds += deltaseconds;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AIntegrationGameModeBase::OnWorldPostActorTick(UWorld* InWorld, ELevelTick InLevelTick, float deltaseconds)
|
||||||
|
{
|
||||||
|
if(Playing && (GetWorld() == InWorld) && (InLevelTick == LEVELTICK_All))
|
||||||
|
{
|
||||||
|
LuprexUpdateTask.Trigger();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void AIntegrationGameModeBase::Tick(float deltaseconds)
|
void AIntegrationGameModeBase::Tick(float deltaseconds)
|
||||||
{
|
{
|
||||||
Super::Tick(deltaseconds);
|
Super::Tick(deltaseconds);
|
||||||
if (Playing)
|
|
||||||
{
|
|
||||||
EngineSeconds += deltaseconds;
|
|
||||||
}
|
|
||||||
|
|
||||||
UpdateConsoleOutput();
|
UpdateConsoleOutput();
|
||||||
UpdateTangibles();
|
UpdateTangibles();
|
||||||
MaybeTriggerUpdateTask(deltaseconds);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AIntegrationGameModeBase::BeginPlay()
|
void AIntegrationGameModeBase::BeginPlay()
|
||||||
|
|||||||
@@ -42,8 +42,11 @@ public:
|
|||||||
UFUNCTION(BlueprintCallable, Category = "Luprex")
|
UFUNCTION(BlueprintCallable, Category = "Luprex")
|
||||||
int64 GetPlayerId();
|
int64 GetPlayerId();
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "Luprex")
|
||||||
|
void InvokeEngioMove(const FString &action, const FVector &xyz);
|
||||||
|
|
||||||
// Execute a debugging command, typed on the GUI.
|
// Execute a debugging command, typed on the GUI.
|
||||||
void ExecuteDebuggingCommand(const FString &fs);
|
void ExecuteDebuggingCommand(FlxLockedWrapper &w, const FString &fs);
|
||||||
|
|
||||||
// Transfer console output from the Luprex engine to unreal.
|
// Transfer console output from the Luprex engine to unreal.
|
||||||
void UpdateConsoleOutput();
|
void UpdateConsoleOutput();
|
||||||
@@ -51,8 +54,9 @@ public:
|
|||||||
// Update the tangibles according to what Luprex tells us.
|
// Update the tangibles according to what Luprex tells us.
|
||||||
void UpdateTangibles();
|
void UpdateTangibles();
|
||||||
|
|
||||||
// Trigger the update task, if enough time has passed.
|
// Pre-tick and post-tick functions.
|
||||||
void MaybeTriggerUpdateTask(float deltaseconds);
|
void OnWorldPreActorTick(UWorld* InWorld, ELevelTick InLevelTick, float InDeltaSeconds);
|
||||||
|
void OnWorldPostActorTick(UWorld* InWorld, ELevelTick InLevelTick, float InDeltaSeconds);
|
||||||
|
|
||||||
// The run function is called by a background thread
|
// The run function is called by a background thread
|
||||||
// to update luprex sockets and update luprex itself.
|
// to update luprex sockets and update luprex itself.
|
||||||
@@ -83,9 +87,10 @@ public:
|
|||||||
// Amount of elapsed time since BeginPlay.
|
// Amount of elapsed time since BeginPlay.
|
||||||
float EngineSeconds;
|
float EngineSeconds;
|
||||||
|
|
||||||
// When do we next trigger the thread event.
|
|
||||||
float NextThreadTrigger;
|
|
||||||
|
|
||||||
// When do we next rotate the cube.
|
// When do we next rotate the cube.
|
||||||
float NextRotateCube;
|
float NextRotateCube;
|
||||||
|
|
||||||
|
// These allow us to pre-tick and post-tick.
|
||||||
|
FDelegateHandle OnWorldPreActorTickHandle;
|
||||||
|
FDelegateHandle OnWorldPostActorTickHandle;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -96,6 +96,9 @@ void UlxTangible::SetActorBlueprint(const FString &name) {
|
|||||||
|
|
||||||
void UlxTangible::UpdateAnimationQueue(std::string_view aq) {
|
void UlxTangible::UpdateAnimationQueue(std::string_view aq) {
|
||||||
AnimTracker.Update(aq);
|
AnimTracker.Update(aq);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UlxTangible::MaybeExecuteAnimStateChanged() {
|
||||||
int limit = 3;
|
int limit = 3;
|
||||||
while (AnimTracker.IsChanged()) {
|
while (AnimTracker.IsChanged()) {
|
||||||
if (limit == 0) break;
|
if (limit == 0) break;
|
||||||
|
|||||||
@@ -130,13 +130,15 @@ public:
|
|||||||
|
|
||||||
// Update the animation queue from Luprex.
|
// Update the animation queue from Luprex.
|
||||||
//
|
//
|
||||||
// This reads the animation queue, and then based on
|
// This reads the animation queue, and copies it
|
||||||
// what is new in the animation queue, it calls into
|
// into the animtracker.
|
||||||
// the Actor's TangibleInterface, calling methods such
|
|
||||||
// as 'StartAnimation' and 'AbortAnimation' as necessary.
|
|
||||||
//
|
//
|
||||||
void UpdateAnimationQueue(std::string_view aq);
|
void UpdateAnimationQueue(std::string_view aq);
|
||||||
|
|
||||||
|
// Execute AnimStateChanged, if the 'changed' bit is set.
|
||||||
|
//
|
||||||
|
void MaybeExecuteAnimStateChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Set the actor's blueprint, and recreate the actor if necessary.
|
// Set the actor's blueprint, and recreate the actor if necessary.
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -7,23 +7,21 @@ FTriggeredTask::FTriggeredTask() {
|
|||||||
Client = nullptr;
|
Client = nullptr;
|
||||||
Thread = nullptr;
|
Thread = nullptr;
|
||||||
ThreadStopRequested = false;
|
ThreadStopRequested = false;
|
||||||
ThreadEvent = nullptr;
|
CallEvent = nullptr;
|
||||||
|
ReturnEvent = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32 FTriggeredTask::Run() {
|
uint32 FTriggeredTask::Run() {
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
bool triggered = ThreadEvent->Wait(3000);
|
CallEvent->Wait();
|
||||||
if (ThreadStopRequested) {
|
if (ThreadStopRequested) {
|
||||||
DPrint("Thread stopping as requested");
|
DPrint("Thread stopping as requested");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (!triggered) {
|
|
||||||
DPrint("Thread waiting a long time...");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// The payload.
|
// The payload.
|
||||||
Client->Run();
|
Client->Run();
|
||||||
|
ReturnEvent->Trigger();
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -32,7 +30,9 @@ void FTriggeredTask::Startup(FRunnable *client) {
|
|||||||
FScopeLock lock(&Mutex);
|
FScopeLock lock(&Mutex);
|
||||||
if (Thread == nullptr) {
|
if (Thread == nullptr) {
|
||||||
Client = client;
|
Client = client;
|
||||||
ThreadEvent = FPlatformProcess::GetSynchEventFromPool(false);
|
CallEvent = FPlatformProcess::GetSynchEventFromPool(true);
|
||||||
|
ReturnEvent = FPlatformProcess::GetSynchEventFromPool(false);
|
||||||
|
ReturnEvent->Trigger();
|
||||||
Thread = FRunnableThread::Create(this, TEXT("Worker Thread"));
|
Thread = FRunnableThread::Create(this, TEXT("Worker Thread"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -40,12 +40,15 @@ void FTriggeredTask::Startup(FRunnable *client) {
|
|||||||
void FTriggeredTask::Shutdown() {
|
void FTriggeredTask::Shutdown() {
|
||||||
FScopeLock lock(&Mutex);
|
FScopeLock lock(&Mutex);
|
||||||
if (Thread != nullptr) {
|
if (Thread != nullptr) {
|
||||||
|
ReturnEvent->Wait();
|
||||||
ThreadStopRequested = true;
|
ThreadStopRequested = true;
|
||||||
ThreadEvent->Trigger();
|
CallEvent->Trigger();
|
||||||
delete Thread; // This waits for the thread to complete.
|
delete Thread; // This waits for the thread to complete.
|
||||||
Thread = nullptr;
|
Thread = nullptr;
|
||||||
FPlatformProcess::ReturnSynchEventToPool(ThreadEvent);
|
FPlatformProcess::ReturnSynchEventToPool(CallEvent);
|
||||||
ThreadEvent = nullptr;
|
FPlatformProcess::ReturnSynchEventToPool(ReturnEvent);
|
||||||
|
CallEvent = nullptr;
|
||||||
|
ReturnEvent = nullptr;
|
||||||
}
|
}
|
||||||
ThreadStopRequested = false;
|
ThreadStopRequested = false;
|
||||||
}
|
}
|
||||||
@@ -53,7 +56,15 @@ void FTriggeredTask::Shutdown() {
|
|||||||
void FTriggeredTask::Trigger() {
|
void FTriggeredTask::Trigger() {
|
||||||
FScopeLock lock(&Mutex);
|
FScopeLock lock(&Mutex);
|
||||||
if (Thread != nullptr) {
|
if (Thread != nullptr) {
|
||||||
ThreadEvent->Trigger();
|
ReturnEvent->Reset();
|
||||||
|
CallEvent->Trigger();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FTriggeredTask::Wait() {
|
||||||
|
FScopeLock lock(&Mutex);
|
||||||
|
if (Thread != nullptr) {
|
||||||
|
ReturnEvent->Wait();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,15 +11,13 @@
|
|||||||
// * You have a function to run in the background.
|
// * You have a function to run in the background.
|
||||||
// * It should start the instant a foreground thread says "NOW".
|
// * It should start the instant a foreground thread says "NOW".
|
||||||
// * It should be run exactly once for each "NOW".
|
// * It should be run exactly once for each "NOW".
|
||||||
|
// * The foreground thread eventually waits for the background thread to finish.
|
||||||
//
|
//
|
||||||
// To use this class, construct an FTriggeredTask,
|
// To use this class, construct an FTriggeredTask,
|
||||||
// and provide a runnable object. The runnable
|
// and provide a runnable object. The runnable
|
||||||
// object's "Run" method will get executed in
|
// object's "Run" method will get executed in
|
||||||
// each time you call 'Trigger' on the FTriggeredTask.
|
// each time you call 'Trigger' on the FTriggeredTask.
|
||||||
//
|
//
|
||||||
// The run method will never get called reentrantly,
|
|
||||||
// even if you call 'Trigger' rapidly in succession.
|
|
||||||
//
|
|
||||||
///////////////////////////////////////////////
|
///////////////////////////////////////////////
|
||||||
|
|
||||||
class FTriggeredTask : public FRunnable
|
class FTriggeredTask : public FRunnable
|
||||||
@@ -43,7 +41,12 @@ private:
|
|||||||
// once. But if ThreadStopRequested is true, it means we
|
// once. But if ThreadStopRequested is true, it means we
|
||||||
// want the thread to exit.
|
// want the thread to exit.
|
||||||
//
|
//
|
||||||
FEvent* ThreadEvent;
|
FEvent* CallEvent;
|
||||||
|
|
||||||
|
// This event is used when the thread is done
|
||||||
|
// with its work.
|
||||||
|
//
|
||||||
|
FEvent* ReturnEvent;
|
||||||
|
|
||||||
// The client whose task we're triggering.
|
// The client whose task we're triggering.
|
||||||
//
|
//
|
||||||
@@ -91,12 +94,18 @@ public:
|
|||||||
// Trigger
|
// Trigger
|
||||||
//
|
//
|
||||||
// Run the client's 'RUN' method once, in the
|
// Run the client's 'RUN' method once, in the
|
||||||
// background. Trigger is a no-op if the system
|
// background.
|
||||||
// is shut down. If you trigger when the task
|
|
||||||
// is already running, a trigger may get dropped.
|
|
||||||
//
|
//
|
||||||
void Trigger();
|
void Trigger();
|
||||||
|
|
||||||
|
// Wait
|
||||||
|
//
|
||||||
|
// Wait for the background task to finish its work.
|
||||||
|
// If the background thread isn't running, then this
|
||||||
|
// returns immediately.
|
||||||
|
//
|
||||||
|
void Wait();
|
||||||
|
|
||||||
// IsRunning
|
// IsRunning
|
||||||
//
|
//
|
||||||
bool IsRunning();
|
bool IsRunning();
|
||||||
|
|||||||
Reference in New Issue
Block a user