first commit

This commit is contained in:
2025-07-03 19:02:47 +02:00
parent e32143bba7
commit 8d8d6f0103
324 changed files with 6415 additions and 0 deletions

View File

@ -0,0 +1,44 @@
// Copyright Epic Games, Inc. All Rights Reserved.
using UnrealBuildTool;
public class DTFluxAPI : ModuleRules
{
public DTFluxAPI(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
PublicDependencyModuleNames.AddRange(
new string[]
{
"Core", "AvalancheText",
}
);
PrivateDependencyModuleNames.AddRange(
new string[]
{
"CoreUObject",
"Engine",
"EditorSubsystem",
"EditorFramework",
"Slate",
"SlateCore",
"HTTPServer",
"HTTP",
"Networking",
"WebSockets",
"WebSocketNetworking",
"DeveloperToolSettings",
"DeveloperSettings",
"Json",
"JsonUtilities",
"SlateCore",
"Text3D",
"AvalancheCore",
"AvalancheMedia",
"AvalancheText",
}
);
}
}

View File

@ -0,0 +1,22 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#include "DTFluxAPI.h"
#include "DTFluxAPILog.h"
#define LOCTEXT_NAMESPACE "FDTFluxAPIModule"
DEFINE_LOG_CATEGORY(LogDTFluxAPI);
void FDTFluxAPIModule::StartupModule()
{
// This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module
}
void FDTFluxAPIModule::ShutdownModule()
{
// This function may be called during shutdown to clean up your module. For modules that support dynamic reloading,
// we call this function before unloading the module.
}
#undef LOCTEXT_NAMESPACE
IMPLEMENT_MODULE(FDTFluxAPIModule, DTFluxAPI)

View File

@ -0,0 +1,92 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "DTFluxCountDown/DTFluxCountDownComponent.h"
#include "DTFluxAPILog.h"
#include "DTFluxSubsystem/DTFluxSubsystem.h"
// Sets default values for this component's properties
UDTFluxCountDownComponent::UDTFluxCountDownComponent()
{
// Set this component to be initialized when the game starts, and to be ticked every frame. You can turn these features
// off to improve performance if you don't need them.
PrimaryComponentTick.bCanEverTick = true;
if(FModuleManager::Get().IsModuleLoaded("DTFluxApi"))
{
DataStorage = GEngine->GetEngineSubsystem<UDTFluxSubsystem>()->GetDataStorage();
UE_LOG(LogDTFluxAPI, Log, TEXT("DTFluxApi loaded"))
}else
{
UE_LOG(LogDTFluxAPI, Error, TEXT("DTFluxApi Not Loaded"))
}
// ...
}
// Called when the game starts
void UDTFluxCountDownComponent::BeginPlay()
{
Super::BeginPlay();
UWorld* World = GetWorld();
if(World)
{
World->GetTimerManager().
SetTimer(WaitingTimer, this,
&UDTFluxCountDownComponent::WaitingTimerFn, WaitingRate, true);
}
// ...
}
// Called every frame
void UDTFluxCountDownComponent::TickComponent(float DeltaTime, ELevelTick TickType,
FActorComponentTickFunction* ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
// ...
}
void UDTFluxCountDownComponent::SetGoTime(FDateTime NewGoTime)
{
GoTime = NewGoTime;
}
void UDTFluxCountDownComponent::SetDuration(int32 NewDuration)
{
// Need to be protected while counting
Duration = NewDuration;
}
void UDTFluxCountDownComponent::SetTarget(UAvaText3DComponent* TextComponent)
{
// Need to be protected while counting
TextRef = TextComponent;
if(IsValid(TextRef))
{
FText WaitingText = FText::FromString("WAITING !!!");
TextRef->SetText(WaitingText);
UE_LOG(LogDTFluxAPI, Log, TEXT("Setting TextRef to %s"), *TextRef->GetText().ToString());
}
}
void UDTFluxCountDownComponent::CountUpTimerFn()
{
}
void UDTFluxCountDownComponent::WaitingTimerFn()
{
// UE_LOG(LogDTFluxAPI, Log, TEXT("WAITING"));
}

View File

@ -0,0 +1,666 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "DTFluxDataStorage/DTFluxDataStorage.h"
#include "DTFluxAPILog.h"
#include "DTFluxModel/DTFluxModel.h"
TArray<FDTFluxStage> UDTFluxDataStorage::GetStages(const int ContestId)
{
TArray<FDTFluxStage> Stages;
for(const auto& Contest : Contests)
{
Stages.Append(Contest.Stages);
}
return Stages;
}
void UDTFluxDataStorage::UpdateSplitRanking(const FDTFluxStageRankingResponse& SplitRankingResponse)
{
for(auto& Contest : Contests)
{
if(Contest.Id == SplitRankingResponse.ContestID)
{
for(auto& Stage : Contest.Stages)
{
if(Stage.Id == SplitRankingResponse.StageID)
{
for(auto& Split : Stage.Splits)
{
if(Split.Id == SplitRankingResponse.SplitID)
{
for(auto& SplitRankingItemResp : SplitRankingResponse.Datas)
{
Split.InsertOrReplace(SplitRankingItemResp);
}
}
}
}
}
}
}
}
// // Bad implementation
// void UDTFluxDataStorage::UpdateParticipant(const FDTFluxTeamUpdateResponse& TeamUpdateResponse)
// {
// FDTFluxParticipant Participant;
//
// for(auto& TeamUpdateRespItem : TeamUpdateResponse.Datas)
// {
// Participant.Bib = TeamUpdateRespItem.Bib;
// Participant.Category = TeamUpdateRespItem.Category;
// Participant.Club = TeamUpdateRespItem.Club;
// Participant.Elite = TeamUpdateRespItem.Elite;
// Participant.Person1.Gender = TeamUpdateRespItem.Gender;
// Participant.Person1.FirstName = TeamUpdateRespItem.FirstName;
// Participant.Person1.LastName = TeamUpdateRespItem.LastName;
// // TODO ???
// // Participant.Person2.Gender = TeamListItemResponse.Gender2;
// Participant.Person2.FirstName = TeamUpdateRespItem.FirstName2;
// Participant.Person2.LastName = TeamUpdateRespItem.LastName2;
// Participant.Status = static_cast<TEnumAsByte<EDTFluxParticipantStatusType>>(TeamUpdateRespItem.Status);
// bool Update = false;
// for(auto& Contest : Contests)
// {
// int Idx = 0;
// for(auto& OldParticipant : Contest.Participants)
// {
// if(OldParticipant.Bib == Participant.Bib)
// {
// Update = true;
// UE_LOG(LogDTFluxAPI, Log, TEXT("Idx %d OLD : %s %s New : %s %s In Contest%02d"),
// Idx, *OldParticipant.Person1.FirstName, *OldParticipant.Person1.LastName,
// *Participant.Person1.FirstName, *Participant.Person1.LastName, Contest.Id);
// break;
// }
// Idx++;
// }
// if(Update)
// {
// if(Contest.Participants.IsValidIndex(Idx))
// {
// UE_LOG(LogDTFluxAPI, Log, TEXT("Idx %d, to be removed : %s %s in Contest%02d"),
// Idx, *Contest.Participants[Idx].Person1.FirstName, *Contest.Participants[Idx].Person1.LastName,
// Contest.Id);
// Contest.Participants.RemoveAt(Idx);
// }
// }
// Contest.Participants.Add(Participant);
//
// }
// }
// }
// TODO NOT IMPLEMENTED
void UDTFluxDataStorage::UpdateParticipantStatus(const FDTFluxStatusUpdateResponse& StatusUpdateResponse)
{
EDTFluxParticipantStatusType NewStatus = static_cast<TEnumAsByte<EDTFluxParticipantStatusType>>(StatusUpdateResponse.Datas.Status);
UE_LOG(LogDTFluxAPI, Log, TEXT("Status to be UPDATED for Bib %d to %s"), StatusUpdateResponse.Datas.Bib,
*UEnum::GetValueAsString(NewStatus));
bool Found = false;
for(auto& Contest : Contests)
{
UE_LOG(LogDTFluxAPI, Log, TEXT("Checking Participant witg Bib %d in Contest %02d"),
StatusUpdateResponse.Datas.Bib, Contest.Id);
for(auto& Participant: Contest.Participants)
{
if(Participant.Bib == StatusUpdateResponse.Datas.Bib)
{
Found = true;
EDTFluxParticipantStatusType OldStatus = Participant.Status;
Participant.Status = static_cast<TEnumAsByte<EDTFluxParticipantStatusType>>(StatusUpdateResponse.Datas.Status);
UE_LOG(LogDTFluxAPI, Log, TEXT("Status UPDATED from %s to %s"),
*UEnum::GetValueAsString(OldStatus), *UEnum::GetValueAsString(Participant.Status));
return;
}
}
}
}
// TODO NOT IMPLEMENTED
bool UDTFluxDataStorage::IsFinisherSplit(const FDTFluxSplitSensorResponse& SplitSensorResponse)
{
return true;
}
// TODO NOT IMPLEMENTED
FDTFluxFinisher UDTFluxDataStorage::GetFinisherStatus(const FDTFluxSplitSensorResponse& SplitSensorResponse)
{
FDTFluxFinisher Finisher;
Finisher.Participant.Bib = 15222;
return Finisher;
}
bool UDTFluxDataStorage::GetContest(const int ContestId, FDTFluxContest& OutContest )
{
// UE_LOG(LogDTFluxAPI, Warning, TEXT("RequestedContest %d"),
// ContestId);
for(auto& Contest : Contests)
{
// UE_LOG(LogDTFluxAPI, Warning, TEXT("Checking Contest %d"),
// Contest.Id);
if(Contest.Id == ContestId)
{
// UE_LOG(LogDTFluxAPI, Warning, TEXT("Found Contest %d"),
// Contest.Id);
OutContest = Contest;
return true;
}
}
// UE_LOG(LogDTFluxAPI, Error, TEXT("Contest %d not Found "),
// ContestId);
return false;
}
bool UDTFluxDataStorage::GetStage(const int ContestId, const int StageId, FDTFluxStage& OutStage)
{
FDTFluxContest Contest;
// UE_LOG(LogDTFluxAPI, Warning, TEXT("RequestedStage %d in Contest%02d ****"),
// ContestId, StageId);
if(GetContest(ContestId, Contest))
{
for(auto & Stage: Contest.Stages)
{
// UE_LOG(LogDTFluxAPI, Warning, TEXT("Checking Stage %d "),
// Stage.Id);
if(Stage.Id == StageId)
{
// UE_LOG(LogDTFluxAPI, Warning, TEXT("Found %s in Stage ***"),
// *Stage.Name);
OutStage = Stage;
return true;
}
}
}
// UE_LOG(LogDTFluxAPI, Error, TEXT("Stage %d Not Found in Contest %d ****"),
// StageId, ContestId);
return false;
}
bool UDTFluxDataStorage::GetSplit(const int ContestId, const int StageId, const int SplitId, FDTFluxSplit& OutSplit)
{
// DumpContest();
FDTFluxStage Stage;
// UE_LOG(LogDTFluxAPI, Warning, TEXT("RequestedSplit %d in Stage%02d of Contest%02d"),
// ContestId, StageId, SplitId);
if(GetStage(ContestId, StageId, Stage))
{
for(auto& Split: Stage.Splits)
{
if(Split.Id == SplitId)
{
// UE_LOG(LogDTFluxAPI, Warning, TEXT("Get Split %s in Stage%02d of Contest%02d"),
// *Split.Name, StageId, SplitId);
OutSplit = Split;
return true;
}
}
}
return false;
}
FDTFluxSplitRanking UDTFluxDataStorage::AddSplitRanking(const FDTFluxSplitSensorItemResponse& SplitSensorItem)
{
FDTFluxSplitRanking NewSplitRanking;
NewSplitRanking.Bib = SplitSensorItem.Bib;
NewSplitRanking.Gap = SplitSensorItem.Gap;
NewSplitRanking.Rank = SplitSensorItem.Rank;
NewSplitRanking.Time = SplitSensorItem.Time;
NewSplitRanking.ContestId = SplitSensorItem.ContestID;
NewSplitRanking.StageId = SplitSensorItem.StageID;
NewSplitRanking.SplitId = SplitSensorItem.SplitID;
FDTFluxSplit Split;
if(GetSplit(SplitSensorItem.ContestID, SplitSensorItem.StageID,
SplitSensorItem.SplitID, Split))
{
Split.SplitRankings.Add(NewSplitRanking);
return NewSplitRanking;
}
UE_LOG(LogDTFluxAPI, Error,
TEXT("Error, Cannot process split sensor."))
UE_LOG(LogDTFluxAPI, Error, TEXT("Split %d from stage %d of Contest %d does not exist"),
SplitSensorItem.SplitID, SplitSensorItem.StageID, SplitSensorItem.ContestID);
return NewSplitRanking;
}
EDTFluxSplitType UDTFluxDataStorage::GetSplitStatus(int ContestID, int StageID, int SplitID)
{
FDTFluxStage Stage;
if(GetStage(ContestID, StageID, Stage))
{
int SplitCount = Stage.Splits.Num();
FDTFluxSplit S;
return Stage.GetSplitType(SplitID);
}
return EDTFluxSplitType::UnknownSplitType;
}
bool UDTFluxDataStorage::GetStageRankingForBib(int ContestID, int StageID, int Bib,
FDTFluxStageRanking& OutStageRanking)
{
FDTFluxStage Stage;
GetStage(ContestID, StageID, Stage);
for(auto& StageRanking : Stage.StageRanking)
{
if(StageRanking.Bib == Bib )
{
OutStageRanking = StageRanking;
return true;
}
}
return false;
}
TArray<FDTFluxParticipant> UDTFluxDataStorage::GetParticipants(const int ContestId)
{
TArray<FDTFluxParticipant> Participants;
for(const auto& Contest : Contests)
{
if (ContestId <= -1)
{
Participants.Append(Contest.Participants);
}
else if(ContestId == Contest.Id)
{
Participants.Append(Contest.Participants);
}
}
return Participants;
}
void UDTFluxDataStorage::GetParticipant(const int ContestID, const int ParticipantBib, FDTFluxParticipant& OutParticipant)
{
TArray<FDTFluxParticipant> Particpants = GetParticipants(ContestID);
for(auto& Participant : Particpants)
{
if(Participant.Bib == ParticipantBib)
{
OutParticipant = Participant;
}
}
}
TArray<FDTFluxStageRanking> UDTFluxDataStorage::GetStageRanking(const int ContestId, const int StageId)
{
TArray<FDTFluxStageRanking> StageRanking;
FDTFluxStage Stage;
if(GetStage(ContestId, StageId, Stage))
{
Stage.StageRanking.Sort([](FDTFluxStageRanking A, FDTFluxStageRanking B)
{
return A.Rank < B.Rank;
});
return Stage.StageRanking;
}
return StageRanking;
}
void UDTFluxDataStorage::AddOrUpdateContest(const FDTFluxContestResponse& ContestResponse)
{
FDTFluxContest Contest;
bool NewContest = false;
int ContestIdx = 0;
// UE_LOG(LogDTFluxAPI, Warning, TEXT("DateTime Json Contest \"%s\""), *ContestResponse.Date.ToString() );
if(!Contests.IsEmpty() )
{
for(auto& OldContest: Contests)
{
NewContest = true;
if(OldContest.Id == ContestResponse.Id)
{
// We have the contest that need to be updated
Contest = OldContest;
NewContest = false;
break;
}
// updating ContestIndex
ContestIdx++;
}
}else
{
// this is a new contest because we don't have one
NewContest = true;
}
// Updating values
Contest.Id = ContestResponse.Id;
Contest.Name = ContestResponse.Name;
Contest.Date = ContestResponse.Date;
TArray<FDTFluxSplit> Splits;
for(auto Split: ContestResponse.Splits)
{
FDTFluxSplit S;
S.Id = Split.Id;
S.Name = Split.Name;
Splits.Add(S);
}
for(auto StageResp : ContestResponse.Stages )
{
FDTFluxStage Stage;
Stage.Id = StageResp.Id;
Stage.Name = StageResp.Name;
// UE_LOG(LogDTFluxAPI, Warning, TEXT("ContestResponse.Stage StartTime = %s"), *StageResp.StartTime);
// UE_LOG(LogDTFluxAPI, Warning, TEXT("ContestResponse.Stage EndTime = %s"), *StageResp.EndTime);
// UE_LOG(LogDTFluxAPI, Warning, TEXT("ContestResponse.Stage CutOff = %s"), *StageResp.CutOff);
FTimespan StartTimeSpan;
FTimespan::Parse(StageResp.StartTime, StartTimeSpan);
FTimespan EndTimeSpan;
FTimespan::Parse(StageResp.EndTime, EndTimeSpan);
FTimespan CutOffTimeSpan;
FTimespan::Parse(StageResp.CutOff, CutOffTimeSpan);
Stage.StartTime = Contest.Date + StartTimeSpan;
Stage.EndTime = Contest.Date + EndTimeSpan;
Stage.CutOff = Stage.StartTime + CutOffTimeSpan;
// UE_LOG(LogDTFluxAPI, Warning, TEXT("STAGE StartTime = %s"), *Stage.StartTime.ToString());
// UE_LOG(LogDTFluxAPI, Warning, TEXT("STAGE EndTime = %s"), *Stage.EndTime.ToString());
// UE_LOG(LogDTFluxAPI, Warning, TEXT("STAGE CutOff = %s"), *Stage.CutOff.ToString());
Stage.Splits = Splits;
Contest.Stages.Add(Stage);
}
if(NewContest)
{
// Contest.Dump();
Contests.Add(Contest);
return;
}
// Contest.Dump();
Contests.RemoveAt(ContestIdx);
Contests.Insert(Contest, ContestIdx);
// handle updating contest
}
void UDTFluxDataStorage::AddOrUpdateParticipant(const FDTFluxTeamListItemResponse& TeamListItemResponse)
{
// UE_LOG(LogDTFluxAPI, Log, TEXT("In DataStorage::AddOrUpdateParticipant"));
// UE_LOG(LogDTFluxAPI, Log, TEXT("AboutToUpdateOrAdd Participant %d %s %s in Contest%02d "),
// TeamListItemResponse.Bib, *TeamListItemResponse.FirstName, *TeamListItemResponse.LastName,
// TeamListItemResponse.ContestId);
FDTFluxParticipant Participant;
Participant.Bib = TeamListItemResponse.Bib;
Participant.Category = TeamListItemResponse.Category;
Participant.Club = TeamListItemResponse.Club;
Participant.Elite = TeamListItemResponse.Elite;
Participant.Person1.Gender = TeamListItemResponse.Gender;
Participant.Person1.FirstName = TeamListItemResponse.FirstName;
Participant.Person1.LastName = TeamListItemResponse.LastName;
// TODO ???
// Participant.Person2.Gender = TeamListItemResponse.Gender2;
Participant.Person2.FirstName = TeamListItemResponse.FirstName2;
Participant.Person2.LastName = TeamListItemResponse.LastName2;
Participant.Status = static_cast<TEnumAsByte<EDTFluxParticipantStatusType>>(TeamListItemResponse.Status);
for(auto& Contest: Contests)
{
if(Contest.Id == TeamListItemResponse.ContestId)
{
// UE_LOG(LogDTFluxAPI, Log, TEXT("AboutToUpdateOrAdd Participant %d %s %s in Contest%02d "),
// Participant.Bib, *Participant.Person1.FirstName, *Participant.Person1.LastName,
// Contest.Id);
Contest.AddParticipant(Participant);
return;
}
// UE_LOG(LogDTFluxAPI, Log, TEXT("Contest%02d has now %04d Participants"), Contest.Id,
// Contest.Participants.Num());
}
}
void UDTFluxDataStorage::UpdateContestRanking(const FDTFluxContestRankingResponse& InContestRanking)
{
for(auto& Contest : Contests)
{
if(Contest.Id == InContestRanking.ContestID)
{
// Clean ContestRanking
Contest.ContestRanking.Empty();
for(const auto& TeamContestRankingResponse : InContestRanking.Datas)
{
FDTFluxContestRanking NewRankingEl;
NewRankingEl.Bib = TeamContestRankingResponse.Bib;
NewRankingEl.Rank = TeamContestRankingResponse.Rank;
NewRankingEl.Gap = TeamContestRankingResponse.Gap;
NewRankingEl.Time = TeamContestRankingResponse.Time;
Contest.ContestRanking.Add(NewRankingEl);
Contest.SortContestRanking();
}
}
}
}
void UDTFluxDataStorage::UpdateStageRanking(const FDTFluxStageRankingResponse& StageRankingResponse)
{
// UE_LOG(LogDTFluxAPI, Log, TEXT("In DataStorage::UdpateStageRanking"));
// UE_LOG(LogDTFluxAPI, Log, TEXT("AboutToUpdate Contest%02d and Stage%02d"),
// StageRankingResponse.ContestID, StageRankingResponse.StageID);
for(auto& Contest: Contests)
{
if(Contest.Id == StageRankingResponse.ContestID)
{
// UE_LOG(LogDTFluxAPI, Log, TEXT("Found Contest::%02d "),Contest.Id);
if(Contest.Stages.IsEmpty())
{
// UE_LOG(LogDTFluxAPI, Log, TEXT("WTF Contest::%02d HAS NO STAGES ???"),Contest.Id);
}
for(auto& Stage: Contest.Stages)
{
// UE_LOG(LogDTFluxAPI, Log, TEXT("Current Stage is Stage::%02d "),Stage.Id);
if(Stage.Id == StageRankingResponse.StageID)
{
// UE_LOG(LogDTFluxAPI, Log, TEXT("Found Stage::%02d "),Stage.Id);
// Cleaning StageRanking
Stage.StageRanking.Empty();
for(auto& StageRankingResp: StageRankingResponse.Datas )
{
FDTFluxStageRanking NewStageRanking;
NewStageRanking.TimeRun = StageRankingResp.TimeRun;
FTimespan StartTimeSpan;
FTimespan::Parse(StageRankingResp.TimeStart, StartTimeSpan);
NewStageRanking.Time = StageRankingResp.Time;
NewStageRanking.TimeStart = Contest.Date + StartTimeSpan;
NewStageRanking.TimeTransition = StageRankingResp.TimeTransition;
NewStageRanking.TimeSwim = StageRankingResp.TimeSwim;
NewStageRanking.Bib = StageRankingResp.Bib;
NewStageRanking.Gap = StageRankingResp.Gap;
NewStageRanking.Rank = StageRankingResp.Rank;
NewStageRanking.SpeedRunning = StageRankingResp.SpeedRunning;
NewStageRanking.SpeedSwim = StageRankingResp.SpeedSwim;
NewStageRanking.SpeedTotal = StageRankingResp.SpeedTotal;
Stage.StageRanking.Add(NewStageRanking);
Stage.SortStageRanking();
// UE_LOG(LogDTFluxAPI, Log,
// TEXT("Testing -> Stage %02d::%s in Contest %02d::%s, has now %02d Rankings\n"),
// Stage.Id, *Stage.Name, Contest.Id, *Contest.Name, Stage.StageRanking.Num());
}
}
}
}
}
}
void UDTFluxDataStorage::AddSplitSensorResult(FDTFluxSplitSensorItemResponse Response)
{
// Send SplitSensor Result to BP
}
void UDTFluxDataStorage::GoToNextStage()
{
// If Number of stages is less or equal to the current stageID
if(IsInitialized())
{
if(Contests[CurrentContestId].Stages.Num() -1 <= CurrentStageId)
{
CurrentStageId += 1;
}else
{
ResetStageId();
ChangeCurrentContest();
}
}
}
void UDTFluxDataStorage::ChangeCurrentContest()
{
// Contest Are initialized
if(IsInitialized())
{
if(CurrentContestId < Contests.Num() -1)
{
// last Contest
CurrentContestId = 0;
}
}
}
const FString UDTFluxDataStorage::GetConcurrentFormatedName(int Bib, bool Truncate, int MaxSize)
{
{
for(const auto& Participant : GetParticipants())
{
if(Participant.Bib == Bib)
{
return Participant.GetParticipantFormatedName(Truncate, MaxSize);
}
}
return "";
};
}
// ReSharper disable once IdentifierTypo
TArray<FDTFluxStageRanking> UDTFluxDataStorage::GetPoursuitWithStageTime(const TArray<int> ContestIds, const int StageId, float DelaTimeSeconds)
{
TArray<int> RemoveIdx;
int Idx = 0;
TArray<FDTFluxStageRanking> StagesRankingsTemp;
TArray<FDTFluxStageRanking> ReturnStageRankings;
FDateTime PresumedStartStage;
for(const int ContestId : ContestIds)
{
FDTFluxStage StageTemp;
if(GetStage(ContestId, StageId, StageTemp))
{
PresumedStartStage = StageTemp.StartTime;
StagesRankingsTemp .Append(StageTemp.StageRanking);
// ContestsRankings
}
}
StagesRankingsTemp.Sort([](const FDTFluxStageRanking& A, const FDTFluxStageRanking& B)
{
return A.TimeStart < B.TimeStart;
});
FDateTime MassStartDate = PresumedStartStage + FTimespan::FromSeconds(DelaTimeSeconds) ;
for( auto & StageRanking : StagesRankingsTemp)
{
if ( StageRanking.TimeStart < MassStartDate )
{
ReturnStageRankings.Add(StageRanking);
}
}
return ReturnStageRankings;
}
// ReSharper disable once IdentifierTypo
TArray<FDTFluxStageRanking> UDTFluxDataStorage::GetPoursuitWithTimeStart(const TArray<int> ContestIds, const int StageId, float DelaTimeSeconds)
{
TArray<int> RemoveIdx;
int Idx = 0;
TArray<FDTFluxStageRanking> StagesRankingsTemp;
TArray<FDTFluxStageRanking> ReturnStageRankings;
for(const int ContestId : ContestIds)
{
FDTFluxStage StageTemp;
if(GetStage(ContestId, StageId, StageTemp))
{
StagesRankingsTemp .Append(StageTemp.StageRanking);
// ContestsRankings
}
}
StagesRankingsTemp.Sort([](const FDTFluxStageRanking& A, const FDTFluxStageRanking& B)
{
return A.TimeStart < B.TimeStart;
});
FDateTime MassStartDate;
if (!StagesRankingsTemp.IsEmpty())
{
MassStartDate = StagesRankingsTemp[0].TimeStart + FTimespan::FromSeconds(DelaTimeSeconds) ;
}
for( auto & StageRanking : StagesRankingsTemp)
{
if ( StageRanking.TimeStart < MassStartDate )
{
ReturnStageRankings.Add(StageRanking);
}
}
return ReturnStageRankings;
}
bool UDTFluxDataStorage::GetFirstStageOfContest(const int ContestId, FDTFluxStage& Stage)
{
if(Contests.IsEmpty())
{
return false;
}
for (auto& Contest : Contests)
{
if(Contest.Id == ContestId)
{
Contest.Stages.Sort([](const FDTFluxStage& A, const FDTFluxStage& B)
{
return A.Id < B.Id;
});
if(Contest.Stages.IsValidIndex(0))
{
Stage = Contest.Stages[0];
return true;
}
return false;
}
}
return false;
}
void UDTFluxDataStorage::DumpContest()
{
for(const auto& Contest : Contests)
{
UE_LOG(LogDTFluxAPI, Warning, TEXT("Contest%02d with name %s : Date %s\n"),
Contest.Id, *Contest.Name, *Contest.Date.ToString());
// UE_LOG(LogDTFluxAPI, Warning, TEXT("Participants :\n"));
// for(const auto& Participant : Contest.Participants)
// {
// Participant.Dump();
// }
UE_LOG(LogDTFluxAPI, Warning, TEXT("Stages :\n"));
for(const auto& Stage : Contest.Stages)
{
Stage.Dump();
}
UE_LOG(LogDTFluxAPI, Warning, TEXT("ContestRanking :\n"));
for(const auto& ContestRankingItem : Contest.ContestRanking)
{
ContestRankingItem.Dump();
}
}
}

View File

@ -0,0 +1,68 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "DTFluxModel/DTFluxModel.h"
bool FDTFluxParticipant::IsTeam() const
{
return (Person2.FirstName != "");
}
void FDTFluxParticipant::Dump() const{
FString EliteStr = "NO";
if(Elite)
{
EliteStr = "YES";
}
UE_LOG(LogDTFluxAPI, Log, TEXT("PARTICIPANT with bib: %03d"), Bib);
UE_LOG(LogDTFluxAPI, Log, TEXT("Fullname : %s %s"), *Person1.FirstName, *Person1.LastName);
if(IsTeam())
{
UE_LOG(LogDTFluxAPI, Log, TEXT("Teamate : %s %s"), *Person2.FirstName, *Person2.LastName);
UE_LOG(LogDTFluxAPI, Log, TEXT("Team name : %s"), *Team);
}
UE_LOG(LogDTFluxAPI, Log, TEXT("Club : %s, Category : %s, IsElite : %s, Status : %s"),
*Club, *Category, *EliteStr, *UEnum::GetValueAsString(Status));
}
bool FDTFluxStage::SetStartTime(const FDateTime& ContestDate, const FString& TimeString)
{
TArray<FString> TimeTokensStart;
TimeString.ParseIntoArray(TimeTokensStart, TEXT(":"));
const int32 HoursStart = FCString::Atoi(*TimeTokensStart[0]);
const int32 MinutesStart = FCString::Atoi(*TimeTokensStart[1]);
const int32 SecondsStart = FCString::Atoi(*TimeTokensStart[2]);
StartTime = FDateTime(ContestDate.GetYear(), ContestDate.GetMonth(), ContestDate.GetDay(),
HoursStart, MinutesStart, SecondsStart);
UE_LOG(LogDTFluxAPI, Log, TEXT("Setting StartTime For %s to %s"), *Name, *StartTime.ToString());
return true;
}
bool FDTFluxStage::SetEndTime(const FDateTime& ContestDate, const FString& TimeString)
{
TArray<FString> TimeTokens;
TimeString.ParseIntoArray(TimeTokens, TEXT(":"));
const int32 Hours = FCString::Atoi(*TimeTokens[0]);
const int32 Minutes = FCString::Atoi(*TimeTokens[1]);
const int32 Seconds = FCString::Atoi(*TimeTokens[2]);
EndTime = FDateTime(ContestDate.GetYear(), ContestDate.GetMonth(), ContestDate.GetDay(),
Hours, Minutes, Seconds);
UE_LOG(LogDTFluxAPI, Log, TEXT("Setting EndTime For %s to %s"), *Name, *StartTime.ToString());
return true;
}
bool FDTFluxContest::SetDate(const FString& StringDate)
{
TArray<FString> Tokens;
StringDate.ParseIntoArray(Tokens, TEXT("-"));
if(Tokens.Num() != 3)
{
return false;
}
const int32 Year = FCString::Atoi(*Tokens[0]);
const int32 Month = FCString::Atoi(*Tokens[1]);
const int32 Day = FCString::Atoi(*Tokens[2]);
Date = FDateTime(Year, Month, Day);
return true;
}

View File

@ -0,0 +1,4 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "DTFluxModel/DTFluxModelResponse.h"

View File

@ -0,0 +1,22 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "DTFluxProjectSettings/DTFluxProjectSettings.h"
const UDTFluxProjectSettings* UDTFluxProjectSettings::GetDTFluxAPIProjectSettings()
{
return GetDefault<UDTFluxProjectSettings>();
}
void UDTFluxProjectSettings::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
{
Super::PostEditChangeProperty(PropertyChangedEvent);
FString ChangeKey = PropertyChangedEvent.Property->GetName();
UE_LOG(LogDTFluxAPI, Log, TEXT("Settings %s has changed"), *ChangeKey);
OnProjectSettingsModified.Broadcast(ChangeKey, GetDTFluxAPIProjectSettings());
}
UDTFluxProjectSettings::UDTFluxProjectSettings()
{
CategoryName = "DTFlux Settings";
}

View File

@ -0,0 +1,553 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "DTFluxSubsystem/DTFluxSubsystem.h"
#include "DTFluxProjectSettings/DTFluxProjectSettings.h"
#include "DTFluxModel/DTFluxModel.h"
#include "DTFluxAPILog.h"
#include "DTFluxDataStorage/DTFluxDataStorage.h"
#include "JsonObjectConverter.h"
#include "DTFluxSubsystemAPISettings/DTFluxSubsystemAPISettings.h"
/****
* DTFlux subsystem
****/
void UDTFluxSubsystem::Initialize(FSubsystemCollectionBase& Collection)
{
Super::Initialize(Collection);
const UDTFluxProjectSettings* Settings = GetSettings();
LoadConfig(Settings);
WsClient = NewObject<UDTFluxWebSocketClient>(this, UDTFluxWebSocketClient::StaticClass());
WsClient->OnConnectionConnected.AddDynamic(this, &UDTFluxSubsystem::WsConnected );
WsClient->OnConnectionClosed.AddDynamic(this, &UDTFluxSubsystem::WsConnectionClosed );
WsClient->OnConnectionError.AddDynamic(this, &UDTFluxSubsystem::WsConnectionError );
WsClient->OnReceivedMessage.AddDynamic(this, &UDTFluxSubsystem::WsReceivedMessage );
UE_LOG(LogDTFluxAPI, Log, TEXT("Trying to connect to %s:%i"), *SubSettings.WebsocketAddress, SubSettings.WebsocketPort);
WsClient->Connect(SubSettings.WebsocketAddress, SubSettings.WebsocketPort);
DataStorage = NewObject<UDTFluxDataStorage>();
// DataStorage->InitDatastorage();
FDateTime Now = FDateTime::Now();
FDateTime Send1Min = Now + FTimespan::FromMinutes(1);
UE_LOG(LogDTFluxAPI, Log, TEXT("TEST timer timeSpan Duration : %s"), *Send1Min.ToString());
}
void UDTFluxSubsystem::Deinitialize()
{
if(WsClient)
{
if (WsClient->Close())
{
UE_LOG(LogDTFluxAPI, Log, TEXT("WsClient is closed"));
}
UE_LOG(LogDTFluxAPI, Error, TEXT("WsClient can not be closed"));
}
UE_LOG(LogDTFluxAPI, Log, TEXT("WsClient has been GC'ed"));
Super::Deinitialize();
}
void UDTFluxSubsystem::InitDataStorage()
{
}
bool UDTFluxSubsystem::ReloadSubsystem()
{
return Reconnect();
}
bool UDTFluxSubsystem::Reconnect()
{
bool Result = WsClient->Close();
DataStorageRaceDataInit = false;
DataStorageTeamListInit = false;
if(!WsClient->IsConnected())
return WsClient->Connect( SubSettings.WebsocketAddress, SubSettings.WebsocketPort);
return false;
}
void UDTFluxSubsystem::LoadConfig(const UDTFluxProjectSettings* Settings)
{
SubSettings.WebsocketPort = Settings->WebsocketServerPort;
SubSettings.WebsocketAddress = Settings->WebsocketServerAddress;
SubSettings.ProxyAddress = Settings->ProxyAddress;
// SubSettings.ProxyPort = Settings->ProxyPort;
// TMap<FString,FString> SettingsEndpoints;
// SettingsEndpoints.Add(FString("race-data"), Settings->ProxyRaceDataEndpoint);
// SettingsEndpoints.Add(FString("contest-ranking"), Settings->ProxyRankingEndpoint);
// SettingsEndpoints.Add(FString("stage-ranking"), Settings->ProxyRankingEndpoint);
// SettingsEndpoints.Add(FString("team-list"), Settings->ProxyTeamsEndpoint);
// SubSettings.ProxyEndpoints = SettingsEndpoints;
}
// Get project Settings
const UDTFluxProjectSettings* UDTFluxSubsystem::GetSettings()
{
if(const UDTFluxProjectSettings* Settings = UDTFluxProjectSettings::GetDTFluxAPIProjectSettings())
return Settings;
else
{
UE_LOG(LogDTFluxAPI, Error, TEXT("Unable to get DTFlux API settings"));
return nullptr;
}
}
// tick function
void UDTFluxSubsystem::Tick(float DeltaTime)
{
if(Timer.Num() > 0)
{
TArray<FDateTime> Done;
for(auto const& El : Timer)
{
FDateTime Dt = FDateTime::Now();
if(Dt >= El.Key)
{
El.Value.Execute(TEXT("Tick"));
OnTimerTriggered.Broadcast();
UE_LOG(LogDTFluxAPI, Log, TEXT("Execution"));
UE_LOG(LogDTFluxAPI, Log, TEXT("TICK : exec time: %lld == %lld"), El.Key.GetTicks(), Dt.GetTicks());
Done.Add(El.Key);
}
}
if(Done.Num() > 0)
{
UE_LOG(LogDTFluxAPI, Log, TEXT("TICK : Cleaning %i"), Done.Num());
for(auto const& ToDelete: Done)
{
Timer.Remove(ToDelete);
}
}
// UE_LOG(LogDTFluxAPI, Log, TEXT("TICK : Timer Length=%i"), Timer.Num());
}
}
void UDTFluxSubsystem::RequestRaceDatas()
{
WsClient->SendMessage(TEXT("{\"path\": \"race-datas\"}"));
}
void UDTFluxSubsystem::RequestTeamList()
{
WsClient->SendMessage(TEXT("{\"path\": \"team-list\"}"));
}
void UDTFluxSubsystem::RequestContestRanking(const int ContestId)
{
const FString Request = FString::Printf(TEXT("{\"path\": \"contest-ranking\", \"contestID\" : %i}"), ContestId);
UE_LOG(LogDTFluxAPI, Log, TEXT("Sending %s to server"), *Request);
WsClient->SendMessage(Request);
}
void UDTFluxSubsystem::RequestStageRanking(const int ContestId, const int StageId)
{
const FString Request = FString::Printf(TEXT("{\"path\": \"stage-ranking\", \"contestID\" : %i, \"stageID\" : %i}"), ContestId, StageId);
WsClient->SendMessage(Request);
}
void UDTFluxSubsystem::RequestSplitGaps(const int ContestId, const int StageId, const int SplitId)
{
const FString Request =
FString::Printf(TEXT("{\"path\": \"stage-ranking\", \"contestID\" : %i, \"stageID\" : %i, \"splitID\" : %i}"),
ContestId, StageId, SplitId);
WsClient->SendMessage(Request);
}
void UDTFluxSubsystem::UpdateRaceData()
{
RequestRaceDatas();
}
void UDTFluxSubsystem::UpdateTeamList()
{
RequestTeamList();
}
void UDTFluxSubsystem::UpdateTeam()
{
}
void UDTFluxSubsystem::UpdateContestRanking(const int ContestID)
{
RequestContestRanking(ContestID);
}
void UDTFluxSubsystem::UpdateStageRanking(const int ContestID, const int StageID, const int SplitID)
{
if(SplitID == -1)
{
RequestStageRanking(ContestID, StageID);
}
else
{
RequestSplitGaps(ContestID, StageID, SplitID);
}
}
/***
* Timer handling
***/
void UDTFluxSubsystem::BroadcastTimerEvent()
{
OnTimerTriggered.Broadcast();
UE_LOG(LogDTFluxAPI, Log, TEXT("TEST timer trigerred at : %s"), *FDateTime::Now().ToString());
}
void UDTFluxSubsystem::SetTimerEvent(const FDateTime& When)
{
FTimespan TimeSpan = FDateTime::Now() - When;
UE_LOG(LogDTFluxAPI, Log, TEXT("TEST timer timeSpan Duration : %s"), *TimeSpan.GetDuration().ToString());
FOnTimer NewTimer;
// NewTimer.BindUFunction()
// AddTimer(When, )
}
bool UDTFluxSubsystem::AddTimer(FDateTime Time, FOnTimer NewTimer)
{
Timer.Add(Time, NewTimer);
return true;
}
/**
* END TIMER HANDLING
***/
void UDTFluxSubsystem::WsConnected()
{
FDTFluxWsResponseEvent Event;
Event.WsResponseType = EDTFluxResponseType::WsConnected;
Event.RawData = "Connected";
if(WsClient->IsConnected())
{
UE_LOG(LogDTFluxAPI, Log, TEXT("Initializing DataStorage"));
UpdateRaceData();
}
OnWsEvent.Broadcast(Event);
UE_LOG(LogDTFluxAPI, Log, TEXT("Ws Connected"));
}
void UDTFluxSubsystem::WsReceivedMessage( const FString& MessageReceived)
{
FDTFluxWsResponseEvent Event;
Event.WsResponseType = UnknownResponse;
Event.RawData = MessageReceived;
TSharedPtr<FJsonValue> JsonValue;
TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(MessageReceived);
if (FJsonSerializer::Deserialize(Reader, JsonValue))
{
TSharedPtr<FJsonObject> Json = JsonValue->AsObject();
FString Type = Json->GetStringField(TEXT("type"));
if(Type.Contains("race-datas"))
{
FDTFluxRaceDataResponse RaceDataResponse;
if(!FJsonObjectConverter::JsonObjectToUStruct<FDTFluxRaceDataResponse>
(Json.ToSharedRef(), &RaceDataResponse))
{
UE_LOG(LogDTFluxAPI, Error, TEXT("Message %s is not a valid \"race-data\" object"), *MessageReceived);
}
UE_LOG(LogDTFluxAPI, Log, TEXT("Message %s is valid race-data object"), *MessageReceived);
ProcessRaceDataResponse(RaceDataResponse);
if(!DataStorageRaceDataInit)
{
DataStorageRaceDataInit = true;
RequestTeamList();
}
Event.WsResponseType = RaceData;
}
if(Type.Contains("contest-ranking"))
{
FDTFluxContestRankingResponse ContestRankingResponse;
if(!FJsonObjectConverter::JsonObjectToUStruct<FDTFluxContestRankingResponse>
(Json.ToSharedRef(), &ContestRankingResponse))
{
UE_LOG(LogDTFluxAPI, Error, TEXT("Message %s is not a valid \"contest-ranking\" object"), *MessageReceived);
}
ProcessContestRankingResponse(ContestRankingResponse);
Event.WsResponseType = ContestRanking;
}
if(Type.Contains("stage-ranking"))
{
FDTFluxStageRankingResponse StageRankingResponse;
if(!FJsonObjectConverter::JsonObjectToUStruct<FDTFluxStageRankingResponse>
(Json.ToSharedRef(), &StageRankingResponse))
{
UE_LOG(LogDTFluxAPI, Error, TEXT("Message %s is not a valid \"stage-ranking\" object"), *MessageReceived);
}
UE_LOG(LogDTFluxAPI, Log, TEXT("\"stage-ranking\" object received"));
if(StageRankingResponse.SplitID == -1)
{
ProcessStageRankingResponse(StageRankingResponse);
Event.WsResponseType = StageRanking;
}
ProcessSplitRankingResponse(StageRankingResponse);
Event.WsResponseType = SplitRanking;
}
if(Type.Contains("team-list"))
{
FDTFluxTeamListResponse TeamListResponse;
if( !FJsonObjectConverter::JsonObjectToUStruct
<FDTFluxTeamListResponse>(Json.ToSharedRef(), &TeamListResponse))
{
UE_LOG(LogDTFluxAPI, Error, TEXT("Message %s is not a valid team-list object"), *MessageReceived)
}
UE_LOG(LogDTFluxAPI, Log, TEXT("Received team-list data"));
ProcessTeamListResponse(TeamListResponse);
if(!DataStorageTeamListInit)
{
DataStorageTeamListInit = true;
// Initialize contest-rankings
for(const auto& Contest: DataStorage->Contests)
{
RequestContestRanking(Contest.Id);
// Initialize stage-rankings
for(const auto Stage : Contest.Stages)
{
RequestStageRanking(Contest.Id, Stage.Id);
}
}
}
Event.WsResponseType = TeamList;
}
if(Type.Contains("team-update"))
{
FDTFluxTeamUpdateResponse TeamUpdateResponse;
if( !FJsonObjectConverter::JsonObjectToUStruct
<FDTFluxTeamUpdateResponse>(Json.ToSharedRef(), &TeamUpdateResponse))
{
UE_LOG(LogDTFluxAPI, Error, TEXT("Message %s is not a valid team-update object"), *MessageReceived)
}
UE_LOG(LogDTFluxAPI, Log, TEXT("Received team-update data"));
ProcessTeamUpdateResponse(TeamUpdateResponse);
Event.WsResponseType = TeamUpdate;
}
if(Type.Contains("split-sensor"))
{
FDTFluxSplitSensorResponse SplitSensorResponse;
if( !FJsonObjectConverter::JsonObjectToUStruct
<FDTFluxSplitSensorResponse>(Json.ToSharedRef(), &SplitSensorResponse))
{
UE_LOG(LogDTFluxAPI, Error, TEXT("Message %s is not a valid split-sensor data"), *MessageReceived)
}
UE_LOG(LogDTFluxAPI, Log, TEXT("Received split-sensor data"));
ProcessSplitSensor(SplitSensorResponse);
Event.WsResponseType = SplitSensor;
}
if(Type.Contains("status-update"))
{
FDTFluxStatusUpdateResponse StatusUpdateResponse;
if( !FJsonObjectConverter::JsonObjectToUStruct
<FDTFluxStatusUpdateResponse>(Json.ToSharedRef(), &StatusUpdateResponse))
{
UE_LOG(LogDTFluxAPI, Error, TEXT("Message %s is not a valid status-update data"), *MessageReceived)
}
UE_LOG(LogDTFluxAPI, Log, TEXT("Received status-update data %s"), *MessageReceived);
ProcessStatusUpdateResponse(StatusUpdateResponse);
Event.WsResponseType = StatusUpdate;
}
if(Type.Contains("broadcast-message"))
{
FDTFluxArchSelectResponse ArchSelectResponse;
if( !FJsonObjectConverter::JsonObjectToUStruct
<FDTFluxArchSelectResponse>(Json.ToSharedRef(), &ArchSelectResponse))
{
UE_LOG(LogDTFluxAPI, Error, TEXT("Message %s is not a valid broadcast-message data"), *MessageReceived)
}
for(const auto& ArchSelect : ArchSelectResponse.Datas)
{
ProcessArchSelect(ArchSelect);
}
Event.RawData = "ArchSelect";
Event.WsResponseType = ArchSelect;
UE_LOG(LogDTFluxAPI, Log, TEXT("Received broadcast-message data"));
}
}
OnWsEvent.Broadcast(Event);
}
void UDTFluxSubsystem::WsConnectionClosed(const FString& Reason)
{
UE_LOG(LogDTFluxAPI, Log, TEXT("Ws ConnectionClosed with reason %s trying to reconnect"), *Reason);
if(!WsClient->IsConnected()){}
WsClient->Connect( SubSettings.WebsocketAddress, SubSettings.WebsocketPort);
FDTFluxWsResponseEvent Event;
Event.WsResponseType = WsClosed;
Event.RawData = Reason;
OnWsEvent.Broadcast(Event);
}
void UDTFluxSubsystem::WsConnectionError(const FString& Error)
{
UE_LOG(LogDTFluxAPI, Log, TEXT("Ws Error %s trying to reconnect"), *Error);
FDTFluxWsResponseEvent Event;
Event.WsResponseType = WsError;
Event.RawData = Error;
OnWsEvent.Broadcast(Event);
bool Result = WsClient->Close();
DataStorageRaceDataInit = false;
DataStorageTeamListInit = false;
if(!WsClient->IsConnected()){}
WsClient->Connect( SubSettings.WebsocketAddress, SubSettings.WebsocketPort);
}
bool UDTFluxSubsystem::IsConnected() const
{
return WsClient->IsConnected();
}
void UDTFluxSubsystem::ProcessTeamListResponse(const FDTFluxTeamListResponse& TeamListResponse)
{
for( const auto& TeamListItemResponse : TeamListResponse.Datas)
{
DataStorage->AddOrUpdateParticipant(TeamListItemResponse);
}
// for(auto& Contest : DataStorage->Contests)
// {
// Contest.DumpParticipant();
// }
// UE_LOG(LogDTFluxAPI, Log, TEXT("New Particpant list Size %d"), DataStorage->GetParticipants().Num())
}
void UDTFluxSubsystem::ProcessRaceDataResponse(const FDTFluxRaceDataResponse& DataResponse)
{
for(const auto ContestResponse : DataResponse.Datas)
{
DataStorage->AddOrUpdateContest(ContestResponse);
}
FDTFluxWsResponseEvent Event;
Event.WsResponseType = RaceData;
Event.RawData = "race-data";
OnWsEvent.Broadcast(Event);
OnRaceDataReceived.Broadcast();
// UE_LOG(LogDTFluxAPI, Log, TEXT("New Contest Size %d"), DataStorage->Contests.Num())
}
void UDTFluxSubsystem::ProcessContestRankingResponse(const FDTFluxContestRankingResponse& ContestRankingResponse)
{
DataStorage->UpdateContestRanking(ContestRankingResponse);
FDTFluxWsResponseEvent Event;
Event.WsResponseType = ContestRanking;
Event.RawData = "contest-ranking";
OnWsEvent.Broadcast(Event);
}
void UDTFluxSubsystem::ProcessStageRankingResponse(const FDTFluxStageRankingResponse& StageRankingResponse)
{
DataStorage->UpdateStageRanking(StageRankingResponse);
FDTFluxWsResponseEvent Event;
Event.WsResponseType = StageRanking;
Event.RawData = "stage-ranking";
OnWsEvent.Broadcast(Event);
}
void UDTFluxSubsystem::ProcessSplitRankingResponse(const FDTFluxStageRankingResponse& SplitRankingResponse)
{
DataStorage->UpdateSplitRanking(SplitRankingResponse);
FDTFluxWsResponseEvent Event;
Event.WsResponseType = SplitRanking;
Event.RawData = "split-ranking";
OnWsEvent.Broadcast(Event);
}
void UDTFluxSubsystem::ProcessTeamUpdateResponse(const FDTFluxTeamUpdateResponse& TeamUpdateResponse)
{
UE_LOG(LogDTFluxAPI, Log, TEXT("team-update received in c++"));
for(auto& TeamListRespItem: TeamUpdateResponse.Datas)
{
DataStorage->AddOrUpdateParticipant(TeamListRespItem);
}
FDTFluxWsResponseEvent Event;
Event.WsResponseType = TeamUpdate;
Event.RawData = "team-update";
OnWsEvent.Broadcast(Event);
}
void UDTFluxSubsystem::ProcessStatusUpdateResponse(const FDTFluxStatusUpdateResponse& StatusUpdateResponse)
{
UE_LOG(LogDTFluxAPI, Log, TEXT("Processing status-update data"));
DataStorage->UpdateParticipantStatus(StatusUpdateResponse);
FDTFluxWsResponseEvent Event;
Event.WsResponseType = StatusUpdate;
Event.RawData = "status-update";
OnWsEvent.Broadcast(Event);
}
void UDTFluxSubsystem::ProcessSplitSensor(const FDTFluxSplitSensorResponse& SplitSensorResponse)
{
//
for(auto& SplitSensorItem : SplitSensorResponse.Datas)
{
FDTFluxSplitRanking NewRanking = DataStorage->AddSplitRanking(SplitSensorItem);
UE_LOG(LogDTFluxAPI, Log, TEXT("Checking SplitStatus ..."))
EDTFluxSplitType SplitType = DataStorage->GetSplitStatus(SplitSensorItem.ContestID,
SplitSensorItem.StageID, SplitSensorItem.SplitID);
FDTFluxFinisherData Data;
Data.Bib = SplitSensorItem.Bib;
Data.ContestId = SplitSensorItem.ContestID;
Data.StageId = SplitSensorItem.StageID;
Data.SplitRanking = NewRanking;
switch(SplitType)
{
case PreFinnishSplit:
UE_LOG(LogDTFluxAPI, Warning, TEXT("SplitSensor %d for Stage%02d in Contest%02d is a Prefinish Sensor"),
SplitSensorItem.SplitID, SplitSensorItem.StageID, SplitSensorItem.ContestID);
OnSpotter.Broadcast(Data);
break;
case FinishSplit:
UE_LOG(LogDTFluxAPI, Warning, TEXT("SplitSensor %d for Stage%02d in Contest%02d is a Finish Sensor"),
SplitSensorItem.SplitID, SplitSensorItem.StageID, SplitSensorItem.ContestID);
DataStorage->GetStageRankingForBib(SplitSensorItem.ContestID, SplitSensorItem.StageID, SplitSensorItem.Bib, Data.StageRanking);
OnFinisher.Broadcast(Data);
break;
case NormalSplit:
UE_LOG(LogDTFluxAPI, Warning, TEXT("SplitSensor %d for Stage%02d in Contest%02d is a Normal Split"),
SplitSensorItem.SplitID, SplitSensorItem.StageID, SplitSensorItem.ContestID);
OnSplitSensor.Broadcast(NewRanking);
break;
default:
UE_LOG(LogDTFluxAPI, Error, TEXT("SplitSensor %d for Stage%02d in Contest%02d %s"),
SplitSensorItem.SplitID, SplitSensorItem.StageID, SplitSensorItem.ContestID,
*UEnum::GetValueAsString(SplitType));
break;
}
}
FDTFluxWsResponseEvent Event;
Event.WsResponseType = SplitSensor;
Event.RawData = "split-sensor";
OnWsEvent.Broadcast(Event);
}
void UDTFluxSubsystem::ProcessArchSelect(FDTFluxArchSelectResponseItem ArchSelectResponse)
{
OnArchSelect.Broadcast(ArchSelectResponse.ContestId, ArchSelectResponse.StageId);
}
TArray<FDTFluxSplitRanking> UDTFluxSubsystem::SortByRank(TArray<FDTFluxSplitRanking> SplitRankingArray)
{
SplitRankingArray.Sort([](const FDTFluxSplitRanking& A, const FDTFluxSplitRanking& B )
{
return A.Rank < B.Rank;
});
return SplitRankingArray;
}

View File

@ -0,0 +1,207 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "DTFluxSubsystem/DTFluxSubsystemTimer.h"
#include "DTFluxAPILog.h"
#include "DTFluxSubsystem/DTFluxSubsystem.h"
void UDTFluxSubsystemTimer::Initialize(FSubsystemCollectionBase& Collection)
{
Super::Initialize(Collection);
// Not Necessary Anymore
// UDTFluxSubsystem* Subsystem = GetDTFluxSubSystem();
// Subsystem->OnRaceDataReceived.AddDynamic(this, &UDTFluxSubsystemTimer::OnDataStorageInit);
}
void UDTFluxSubsystemTimer::Deinitialize()
{
Super::Deinitialize();
}
void UDTFluxSubsystemTimer::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
void UDTFluxSubsystemTimer::OnDataStorageInit()
{
UE_LOG(LogDTFluxAPI, Log, TEXT("DataStorage Has been Set Or Updated"));
UDTFluxDataStorage* DataStorage = GetDTFluxDataStorage();
for(const auto&Contest : DataStorage->Contests)
{
for (const auto& Stage: Contest.Stages)
{
UWorld* World = GetWorld();
if(World)
{
FDTFluxContestTimerHandle StartContestTimerHandle;
StartContestTimerHandle.Type = EDTFluxTimerEventType::StageStart;
StartContestTimerHandle.ContestId = Contest.Id;
StartContestTimerHandle.StageId = Stage.Id;
FDTFluxContestTimerHandle CutOffContestTimerHandle;
CutOffContestTimerHandle.Type = EDTFluxTimerEventType::StageCutOff;
CutOffContestTimerHandle.ContestId = Contest.Id;
CutOffContestTimerHandle.StageId = Stage.Id;
float StartTimeTriggerSeconds = GetSecondsFrom(Stage.StartTime);
float CutOffTimeTriggerSeconds = GetSecondsFrom(Stage.CutOff);
if( StartTimeTriggerSeconds > 0)
{
UE_LOG(LogDTFluxAPI, Log, TEXT("Can Set Time to %04f Seconds"), StartTimeTriggerSeconds );
World->GetTimerManager().SetTimer(StartContestTimerHandle.Handle, this, &UDTFluxSubsystemTimer::OnStartTimer, StartTimeTriggerSeconds);
World->GetTimerManager().SetTimer(CutOffContestTimerHandle.Handle, this, &UDTFluxSubsystemTimer::OnCutOffTimer, CutOffTimeTriggerSeconds);
Timers.Add(StartContestTimerHandle);
Timers.Add(CutOffContestTimerHandle);
}
else
{
UE_LOG(LogDTFluxAPI, Warning, TEXT("Unable to Set Time to %04f Seconds"), StartTimeTriggerSeconds);
}
}
}
}
}
void UDTFluxSubsystemTimer::AddCutoffTimer(const int ContestID, const int StageID)
{
UWorld* World = GetWorld();
if(World)
{
FTimerHandle Timer;
World->GetTimerManager().SetTimer(Timer, this, &UDTFluxSubsystemTimer::OnStartTimer, 1.0, true);
UE_LOG(LogDTFluxAPI, Warning, TEXT("AddCutoffTimer Added"));
}
UE_LOG(LogDTFluxAPI, Error,
TEXT("UDTFluxSubsystemTimer::AddCutoffTimer Cannot have the World"));
}
void UDTFluxSubsystemTimer::AddStageStartedTimer(const int ContestID, const int StageID)
{
UWorld* World = GetWorld();
if(World)
{
FTimerHandle Timer;
World->GetTimerManager().SetTimer(Timer, this, &UDTFluxSubsystemTimer::OnStartTimer, 1.0, true);
UE_LOG(LogDTFluxAPI, Warning, TEXT("AddStageStartedTimer Added"));
}
UE_LOG(LogDTFluxAPI, Error,
TEXT("UDTFluxSubsystemTimer::AddStageStartedTimer Cannot have the World"));
}
void UDTFluxSubsystemTimer::OnStartTimer()
{
UWorld* World = GetWorld();
if(World)
{
int Idx = 0 ;
for(auto& Timer : Timers)
{
if(Timer.Type == EDTFluxTimerEventType::StageStart)
{
if(World->GetTimerManager().GetTimerRemaining(Timer.Handle) == 0)
{
TArray<int> ContestIds;
ContestIds.Add(Timer.ContestId);
OnStageStarted.Broadcast(ContestIds, Timer.StageId);
break;
}
}
Idx++;
}
if(Timers.IsValidIndex(Idx))
{
Timers.RemoveAt(Idx);
}
}
}
void UDTFluxSubsystemTimer::OnCutOffTimer()
{
UWorld* World = GetWorld();
if(World)
{
int Idx = 0 ;
for(auto& Timer : Timers)
{
if(Timer.Type == EDTFluxTimerEventType::StageCutOff)
{
if(World->GetTimerManager().GetTimerRemaining(Timer.Handle) == 0)
{
TArray<int> ContestIds;
ContestIds.Add(Timer.ContestId);
OnCutoff.Broadcast(ContestIds, Timer.StageId);
break;
}
}
Idx++;
}
if(Timers.IsValidIndex(Idx))
{
Timers.RemoveAt(Idx);
}
}
}
void UDTFluxSubsystemTimer::ClearTimer(FDTFluxContestTimerHandle TimerHandle)
{
UWorld* World = GetWorld();
if(World)
{
World->GetTimerManager().ClearTimer(TimerHandle.Handle);
}
UE_LOG(LogDTFluxAPI, Error, TEXT("Cannot Clear Timer %s of type %s for Stage%02d of Contest%02d"),
*TimerHandle.Handle.ToString(), *UEnum::GetValueAsString(TimerHandle.Type),
TimerHandle.StageId, TimerHandle.ContestId)
}
void UDTFluxSubsystemTimer::ClearTimer(const int HandleIndex)
{
}
void UDTFluxSubsystemTimer::TriggerOnCutOff(const TArray<int>& ContestIds, const int StageId)
{
OnCutoff.Broadcast(ContestIds, StageId);
}
void UDTFluxSubsystemTimer::TriggerStartTime(const TArray<int>& ContestIds, const int StageId)
{
OnStageStarted.Broadcast(ContestIds, StageId);
}
void UDTFluxSubsystemTimer::TriggerStageLoading(const TArray<int>& ContestIds, const int StageId, int DelayBeforeStageStart)
{
OnStageLoading.Broadcast(ContestIds, StageId, DelayBeforeStageStart);
}
void UDTFluxSubsystemTimer::TriggerOnDeleteRequested(const TArray<int>& LineIndex)
{
OnRemoveLineRequested.Broadcast(LineIndex);
}
UDTFluxSubsystem* UDTFluxSubsystemTimer::GetDTFluxSubSystem()
{
return GEngine->GetEngineSubsystem<UDTFluxSubsystem>();
}
UDTFluxDataStorage* UDTFluxSubsystemTimer::GetDTFluxDataStorage()
{
return GetDTFluxSubSystem()->GetDataStorage();
}
float UDTFluxSubsystemTimer::GetSecondsFrom(const FDateTime When)
{
FTimespan Delta = When - FDateTime::Now();
return static_cast<float>(Delta.GetTotalSeconds()) ;
}

View File

@ -0,0 +1,70 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "DTFluxSubsystemAPISettings/DTFluxSubsystemAPISettings.h"
#include "DTFluxAPILog.h"
// DEPRECATED : Now in WS
FString FDTFluxSubsystemAPISettings::GetRaceDataEndpoint(const FDTFluxSubsystemAPISettings* Settings)
{
if(Settings)
{
FString RaceDataEndpoint =
FString::Printf(TEXT("%s/%p"), *Settings->GetProxyBaseEndpoint(), Settings->ProxyEndpoints.FindKey("race-datas"));
UE_LOG(LogDTFluxAPI, Log, TEXT("Proxy Race Data -> %s"), *RaceDataEndpoint);
return RaceDataEndpoint;
}
return FString("");
}
// DEPRECATED : Now in WS
FString FDTFluxSubsystemAPISettings::GetContestRankingEndpoint(const FDTFluxSubsystemAPISettings* Settings, const int ContestId)
{
if(Settings)
{
FString Ranking = *Settings->ProxyEndpoints.FindKey("ranking");
const TCHAR* ContestIDTmpl = *FString("{:ContestID}");
const TCHAR* ContestIDValue = *FString(TEXT("%i"),ContestId);
FString ContestRanking = Ranking.Replace(ContestIDTmpl, ContestIDValue );
FString ContestRankingEndpoint = Settings->GetProxyBaseEndpoint() + ContestRanking;
UE_LOG(LogDTFluxAPI, Log, TEXT("Proxy Contest Ranking -> %s"), *ContestRankingEndpoint);
return ContestRankingEndpoint;
}
return FString("");
}
// DEPRECATED : Now in WS
FString FDTFluxSubsystemAPISettings::GetStageRankingEndpoint(const FDTFluxSubsystemAPISettings* Settings, const int ContestId,
const int StageId)
{
if(Settings)
{
FString StageRanking = GetContestRankingEndpoint(Settings, ContestId);
StageRanking = FString::Printf(TEXT("%s/stage/%i/"), *StageRanking, StageId);
UE_LOG(LogDTFluxAPI, Log, TEXT("Proxy Stage Ranking -> %s"), *StageRanking);
return StageRanking;
}
return FString("");
}
// DEPRECATED : Now in WS
FString FDTFluxSubsystemAPISettings::GetStageRankingFilteredEndpoint(const FDTFluxSubsystemAPISettings* Settings,
const int ContestId, const int StageId, const FString SplitName)
{
if (Settings){
FString StageRanking = GetStageRankingEndpoint(Settings, ContestId, StageId);
StageRanking = FString::Printf(TEXT("%s?splitname=%s"), *StageRanking, *SplitName);
UE_LOG(LogDTFluxAPI, Log, TEXT("Proxy Stage Ranking with Splitname -> %s"), *StageRanking);
return StageRanking;
}
return FString("");
}
// DEPRECATED : Now in WS
FString FDTFluxSubsystemAPISettings::GetTeamsEndpoint(const FDTFluxSubsystemAPISettings* Settings)
{
if(Settings)
{
FString TeamsEndpoint =
FString::Printf(TEXT("%s/%p"), *Settings->GetProxyBaseEndpoint(), Settings->ProxyEndpoints.FindKey("teams"));
UE_LOG(LogDTFluxAPI, Log, TEXT("Proxy Teams -> %s"), *TeamsEndpoint );
return TeamsEndpoint;
}
return FString("");
}

View File

@ -0,0 +1,4 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "DTFluxUtils/DTFluxEnums.h"

View File

@ -0,0 +1,88 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "DTFluxUtils/DTFluxUtils.h"
#include "DTFluxModel/DTFluxModel.h"
EDTFluxStageStatusType UDTFluxModelHelper::GetStatusType(const int ContestID, const int StageID,
UDTFluxDataStorage* DataStorage)
{
EDTFluxStageStatusType StageStatus = UnknownStatus;
FDTFluxStage SelectedStage;
if( DataStorage->GetStage(ContestID, StageID, SelectedStage))
{
StageStatus = StageWaiting;
FDateTime Now = FDateTime::Now();
if(SelectedStage.StartTime <= Now)
{
StageStatus = StageStarted;
}
if(SelectedStage.CutOff <= Now)
{
StageStatus = StageEnded;
}
}
return StageStatus;
}
int UDTFluxModelHelper::GetCurrentContest(UDTFluxDataStorage* DataStorage)
{
int ContestId = -1;
FDateTime Now = FDateTime::Now();
for(const auto& Contest : DataStorage->Contests)
{
for(const auto& Stage : Contest.Stages)
{
// Stage has begun
if(Stage.StartTime <= Now)
{
return Contest.Id;
}
}
}
return ContestId;
}
TArray<int> UDTFluxModelHelper::GetCurrentStage(UDTFluxDataStorage* DataStorage)
{
TArray<int> ContestAndStageId;
FDateTime Now = FDateTime::Now();
for(const auto& Contest : DataStorage->Contests)
{
for(const auto& Stage : Contest.Stages)
{
// Stage has begun
if(Stage.StartTime <= Now)
{
ContestAndStageId.Add(Contest.Id);
ContestAndStageId.Add(Stage.Id);
return ContestAndStageId;
}
}
}
return ContestAndStageId;
}
FString UDTFluxModelHelper::GetCurrentStageName(UDTFluxDataStorage* DataStorage)
{
FString Name;
FDateTime Now = FDateTime::Now();
for(const auto& Contest : DataStorage->Contests)
{
for(const auto& Stage : Contest.Stages)
{
// Stage has begun
if(Stage.StartTime <= Now)
{
Name = FString::Printf(TEXT("Contest %s Stage %s"), *Contest.Name, *Stage.Name);
return Name;
}
}
}
return Name;
}

View File

@ -0,0 +1,120 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "DTFluxWebSocket/DTFluxWebsocketClient.h"
// Fill out your copyright notice in the Description page of Project Settings.
#include "DTFluxWebSocket/DTFluxWebsocketClient.h"
#include "IWebSocket.h"
#include "WebSocketsModule.h"
#include "DTFluxAPILog.h"
// UDTFluxWebSocketClient::~UDTFluxWebSocketClient()
// {
//
// }
void UDTFluxWebSocketClient::Initialize()
{
}
// void UDTFluxWebSocketClient::BeginDestroy()
// {
// // if(Ws->IsConnected())
// // Ws->Close();
// UObject::BeginDestroy();
// }
bool UDTFluxWebSocketClient::Connect(const FString URL, const int Port)
{
FString ServerUrl = FString::Printf(TEXT("%s:%i/"), *URL, Port);
UE_LOG(LogDTFluxAPI, Log, TEXT("[DTFluxWebsocketClient %s"), *ServerUrl);
Ws = FWebSocketsModule::Get().CreateWebSocket(ServerUrl);
Ws->OnConnected().AddLambda([this]()
{
if(IsValid(this) == false) return;
OnConnectionConnectedInternal();
});
Ws->OnClosed().AddLambda([this](int32 StatusCode, const FString& Reason, bool bWasClean)
{
if(IsValid(this) == false) return;
OnConnectionClosedInternal(Reason);
});
Ws->OnConnectionError().AddLambda([this](const FString& Error)
{
if(IsValid(this) == false) return;
OnConnectionErrorInternal(Error);
});
Ws->OnMessage().AddLambda([this](const FString& MessageString)
{
if(IsValid(this) == false) return;
OnReceivedMessageInternal(MessageString);
});
Ws->Connect();
if(Ws->IsConnected())
{
UE_LOG(LogDTFluxAPI, Log, TEXT("Listening"));
return true;
}
return false;
}
void UDTFluxWebSocketClient::Disconnect()
{
Ws->Close();
}
bool UDTFluxWebSocketClient::SendMessage(const FString Message, const bool Broadcast)
{
if(Ws->IsConnected() == false) return false;
Ws->Send(Message);
return true;
}
void UDTFluxWebSocketClient::OnConnectionConnectedInternal() const
{
OnConnectionConnected.Broadcast();
UE_LOG(LogDTFluxAPI, Log, TEXT("WebSocketsSubsystem, [Connected]"));
}
void UDTFluxWebSocketClient::OnConnectionErrorInternal(const FString& Error) const
{
OnConnectionError.Broadcast( Error);
UE_LOG(LogDTFluxAPI, Error, TEXT("WebSocketsSubsystem, [Error] : %s"), *Error);
}
void UDTFluxWebSocketClient::OnConnectionClosedInternal(const FString& Reason) const
{
OnConnectionClosed.Broadcast(Reason);
UE_LOG(LogDTFluxAPI, Log, TEXT("WebSocketsSubsystem, [Closed], Reason : %s"), *Reason);
}
void UDTFluxWebSocketClient::OnReceivedMessageInternal(const FString& Message) const
{
OnReceivedMessage.Broadcast(Message);
// UE_LOG(LogDTFluxAPI, Log, TEXT("WebSocketsSubsystem, [Message Reveived], Message : %s"), *Message);
}
bool UDTFluxWebSocketClient::Close() const
{
if (Ws->IsConnected())
Ws->Close();
return Ws->IsConnected();
}
bool UDTFluxWebSocketClient::IsConnected()
{
return Ws->IsConnected();
}

View File

@ -0,0 +1,246 @@
// // Fill out your copyright notice in the Description page of Project Settings.
//
//
// #include "DTFluxWebSocket/DTFluxWebsocketServer.h"
//
// #include <winsock2.h>
//
// #include "IWebSocket.h"
// #include "WebSocketsModule.h"
// #include "INetworkingWebSocket.h"
// #include "IWebSocketNetworkingModule.h"
// #include "WebSocketNetworkingDelegates.h"
// #include "DTFluxAPILog.h"
// #include "IWebSocketServer.h"
//
//
// UDTFluxWebsocketServer::UDTFluxWebsocketServer()
// {
// Ws = UDTFluxWebsocketServer::GetServer();
// }
//
// UDTFluxWebsocketServer::~UDTFluxWebsocketServer()
// {
//
// }
//
// bool UDTFluxWebsocketServer::ConnectToChannel(const FString URL, const int Port)
// {
// //try to get a WsServer
// if(!Ws)
// Ws = UDTFluxWebsocketServer::GetServer();
// if(Ws)
// {
// FWebSocketClientConnectedCallBack ClientConnectedCb;
// ClientConnectedCb.BindUObject(this, &UDTFluxWebsocketServer::OnConnectionConnectedInternal);
// if(Ws->Init((uint32) Port, ClientConnectedCb, FString("127.0.0.1")) )
// {
// UE_LOG(LogDTFluxAPI, Log, TEXT("Server listening on %s:%i"),*URL, Port);
// if (Ws.IsValid())
// UE_LOG(LogDTFluxAPI, Log, TEXT("Server pointer Is Valid %s"), *Ws->Info());
//
// return true;
// }
// else
// {
// UE_LOG(LogDTFluxAPI, Error, TEXT("Server Init() failed"));
// Ws.Reset();
// ClientConnectedCb.Unbind();
// return false;
// }
// }
// UE_LOG(LogDTFluxAPI, Error, TEXT("Unable to get a WsServer Object"));
//
// return false;
// }
//
// void UDTFluxWebsocketServer::LeaveChannel()
// {
// bIsConnected = false;
// Ws = nullptr;
// }
//
// bool UDTFluxWebsocketServer::SendMessageToAll(const FString Message)
// {
// if(!Ws)
// {
// UE_LOG(LogDTFluxAPI, Error, TEXT("WebSocketServer Not Up"));
// return false;
// }
// if(Clients.Num() <= 0)
// {
// UE_LOG(LogDTFluxAPI, Log, TEXT("No Clients Yet !!!"));
// return false;
// }
// int i = 0;
// for(const auto& Client : Clients)
// {
// const TCHAR* SerializedChar = Message.GetCharArray().GetData();
// int32 Size = FCString::Strlen(SerializedChar);
// int32 Sent = 0;
// uint8* Data = (uint8*)TCHAR_TO_UTF8(SerializedChar);
// if(Client->Send(Data, Size))
// {
// UE_LOG(LogDTFluxAPI, Log, TEXT("Message %s sent to client n°%i"), *Message, i);
// i++;
// }
// }
// if(i != 0)
// {
// UE_LOG(LogDTFluxAPI, Log, TEXT("%i Messages sent"), i);
// return true;
// }
// UE_LOG(LogDTFluxAPI, Error, TEXT("Message has not been sent"));
// return false;
// }
//
// void UDTFluxWebsocketServer::OnConnectionConnectedInternal(INetworkingWebSocket* ClientWebSocket)
// {
// OnConnectionConnected.Broadcast(ChannelName);
// FWebSocketPacketReceivedCallBack PacketReceivedCallBack;
// PacketReceivedCallBack.BindUObject(this, &UDTFluxWebsocketServer::OnReceivedMessageInternal);
// ClientWebSocket->SetReceiveCallBack(PacketReceivedCallBack);
// FWebSocketInfoCallBack InfoCallBack;
// InfoCallBack.BindLambda([this, ClientWebSocket]()
// {
// int i = 0;
//
// for(const auto& Client : Clients)
// {
// int Removed = Clients.Remove(Client);
// if(Removed)
// UE_LOG(LogDTFluxAPI, Log, TEXT("Client Disconnected %i"), i);
// i++;
// }
// });
// ClientWebSocket->SetSocketClosedCallBack(InfoCallBack);
// FWebSocketInfoCallBack InfoCallBackError;
// InfoCallBackError.BindUObject(this, &UDTFluxWebsocketServer::OnConnectionErrorInternal);
// ClientWebSocket->SetErrorCallBack(InfoCallBackError);
// UE_LOG(LogDTFluxAPI, Log, TEXT("WebSocketServer', [Connected], Channel: %s"), *ChannelName);
// }
//
// void UDTFluxWebsocketServer::OnConnectionErrorInternal()
// {
// UE_LOG(LogDTFluxAPI, Error, TEXT("WebSocketServer'"));
// OnConnectionError.Broadcast(TEXT("Channel"), TEXT("UNKNOWN ERROR"));
//
// }
//
// void UDTFluxWebsocketServer::OnReceivedMessageInternal(void* Data, int32 Count)
// {
// FString Message;
// if (Count == 0) // nothing to process
// {
// return;
// }
// const uint8* DataRef = reinterpret_cast<uint8*>(Data);
// const TArray<uint8> MessageData(DataRef, Count);
// const FString JSonData = UTF8_TO_TCHAR(MessageData.GetData());
// OnReceivedMessage.Broadcast(ChannelName, Message);
// UE_LOG(LogDTFluxAPI, Log, TEXT("WebSocketServer, [Message Reveived]\nMessage : %s"), *Message);
//
// }
//
// TUniquePtr<IWebSocketServer> UDTFluxWebsocketServer::GetServer()
// {
// return FModuleManager::Get().LoadModuleChecked<IWebSocketNetworkingModule>(TEXT("WebSocketNetworking")).
// CreateServer();
// }
//
// void UDTFluxWebsocketServer::Close()
// {
// UE_LOG(LogDTFluxAPI, Log, TEXT("WebSocketServer, [Closed()]"));
//
// }
#include "DTFluxWebSocket/DTFluxWebsocketServer.h"
//Fill out your copyright notice in the Description page of Project Settings.
#include "INetworkingWebSocket.h"
#include "IWebSocketNetworkingModule.h"
#include "WebSocketNetworkingDelegates.h"
#include "DTFluxAPILog.h"
UDTFluxServerWebSocket::UDTFluxServerWebSocket()
{
}
UDTFluxServerWebSocket::~UDTFluxServerWebSocket()
{
}
void UDTFluxServerWebSocket::Init(const int& Port, const FString& Url)
{
ServerWebSocket = FModuleManager::Get().LoadModuleChecked<IWebSocketNetworkingModule>(TEXT("WebSocketNetworking")).CreateServer();
FWebSocketClientConnectedCallBack CallBack;
CallBack.BindLambda([this](INetworkingWebSocket* Client)
{
FGuid uuid = FGuid::NewGuid();
ConnectedClients.Add(uuid, Client);
});
if (!ServerWebSocket->Init(Port, CallBack, Url))
{
UE_LOG(LogDTFluxAPI, Error, TEXT("ServerWebSocket Init FAIL"));
ServerWebSocket.Reset();
CallBack.Unbind();
return;
}
UE_LOG(LogDTFluxAPI, Log, TEXT("ServerWebSocket Connected to port %d"), Port);
TickHandle = FTSTicker::GetCoreTicker().AddTicker(FTickerDelegate::CreateLambda([this](float Time)
{
if (ServerWebSocket)
{
ServerWebSocket->Tick();
return true;
}
else
{
return false;
}
}));
}
void UDTFluxServerWebSocket::BeginDestroy()
{
Super::BeginDestroy();
Close();
if (TickHandle.IsValid())
{
FTSTicker::GetCoreTicker().RemoveTicker(TickHandle);
TickHandle.Reset();
}
}
void UDTFluxServerWebSocket::OnWebSocketClientConnected(INetworkingWebSocket* ClientWebSocket)
{
FWebSocketPacketReceivedCallBack CallBack;
CallBack.BindUObject(this, &UDTFluxServerWebSocket::ReceivedRawPacket);
UE_LOG(LogDTFluxAPI, Log, TEXT("ServerWebSocket Connected"));
ClientWebSocket->SetReceiveCallBack(CallBack);
}
void UDTFluxServerWebSocket::ReceivedRawPacket(void* Data, int32 Count)
{
if (Count == 0) // nothing to process
{
return;
}
const uint8* DataRef = reinterpret_cast<uint8*>(Data);
const TArray<uint8> MessageData(DataRef, Count);
const FString JSonData = UTF8_TO_TCHAR(MessageData.GetData());
UE_LOG(LogDTFluxAPI, Log, TEXT("ServerWebSocket received %s"), *JSonData);
OnJsonRecieved.Broadcast(JSonData);
}

View File

@ -0,0 +1,15 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "Modules/ModuleManager.h"
class FDTFluxAPIModule : public IModuleInterface
{
public:
/** IModuleInterface implementation */
virtual void StartupModule() override;
virtual void ShutdownModule() override;
};

View File

@ -0,0 +1,6 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
DECLARE_LOG_CATEGORY_EXTERN(LogDTFluxAPI, Log, All);

View File

@ -0,0 +1,80 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "AvaText3DComponent.h"
#include "Components/ActorComponent.h"
#include "DTFluxDataStorage/DTFluxDataStorage.h"
#include "DTFluxCountDownComponent.generated.h"
UCLASS(ClassGroup=(DTFlux), meta=(BlueprintSpawnableComponent))
class DTFLUXAPI_API UDTFluxCountDownComponent : public UActorComponent
{
GENERATED_BODY()
public:
// Sets default values for this component's properties
UDTFluxCountDownComponent();
UPROPERTY(BlueprintReadWrite, Category="DTFlux|Counter")
FDateTime GoTime;
UPROPERTY(BlueprintSetter=SetDuration, Category="DTFlux|Counter")
int32 Duration;
protected:
// Called when the game starts
virtual void BeginPlay() override;
UPROPERTY()
UAvaText3DComponent* TextRef;
FTimerHandle WaitingTimer;
FTimerHandle ContDownTimer;
int64 InternalDuration;
bool IsWaiting;
bool IsCounting;
UDTFluxDataStorage* DataStorage;
public:
UPROPERTY(BlueprintReadWrite, Category="DTFlux|Counter")
FString EndString;
UPROPERTY(BlueprintReadWrite, Category="DTFlux|Counter")
float WaitingRate = 1.0f;
UPROPERTY(BlueprintReadWrite, Category="DTFlux|Counter")
float CountDownRate = 1.0f;
// Called every frame
virtual void TickComponent(float DeltaTime, ELevelTick TickType,
FActorComponentTickFunction* ThisTickFunction) override;
UFUNCTION(BlueprintCallable, Category="DTFlux|Counter")
void SetGoTime(FDateTime NewGoTime);
UFUNCTION(BlueprintCallable, Category="DTFlux|Counter")
void SetDuration(int32 NewDuration);
// set the current stage
UFUNCTION(BlueprintCallable, Category="DTFlux|Counter")
void SetStage(const int ContestId, const int StageId){};
// set the current contest
UFUNCTION(BlueprintCallable, Category="DTFlux|Counter")
void SetContest(const int ContestId){};
UFUNCTION(BlueprintCallable, Category="DTFlux|Counter")
void SetTarget(UAvaText3DComponent* TextComponent);
UFUNCTION()
void CountUpTimerFn();
UFUNCTION()
void WaitingTimerFn();
};

View File

@ -0,0 +1,133 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "DTFluxModel/DTFluxModel.h"
#include "UObject/Object.h"
#include "DTFluxDataStorage.generated.h"
/**
*
*/
struct FDTFluxStageRanking;
struct FDTFluxTeam;
struct FDTFluxParticipant;
struct FDTFluxStage;
struct FDTFluxContest;
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnDataStorageUpdated, FString, What);
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnDataStorageInit);
UCLASS(BlueprintType, Category="DTFlux|Datastorage")
class DTFLUXAPI_API UDTFluxDataStorage : public UObject
{
GENERATED_BODY()
friend FDTFluxContest;
friend FDTFluxStage;
public:
UPROPERTY(BlueprintAssignable, Category="DTFlux|DataStorage|Event")
FOnDataStorageInit OnDataStorageInit;
UPROPERTY(BlueprintAssignable, Category="DTFlux|DataStorage|Event")
FOnDataStorageUpdated OnDataStorageUpdated;
UPROPERTY(BlueprintReadOnly, Category="DTFlux|DataStorage")
TArray<FDTFluxContest> Contests;
UPROPERTY(BlueprintReadOnly, Category="DTFlux|DataStorage")
int CurrentStageId = 0;
UPROPERTY(BlueprintReadOnly, Category="DTFlux|DataStorage")
int CurrentContestId = 0;
UFUNCTION(BlueprintCallable, Category="DTFlux|DataStorage")
int CurrentContest()
{
if(IsInitialized())
{
return CurrentContestId;
}
return -1;
}
void UpdateSplitRanking(const FDTFluxStageRankingResponse& SplitRankingResponse);
// void UpdateParticipant(const FDTFluxTeamUpdateResponse& TeamUpdateResponse);
void UpdateParticipantStatus(const FDTFluxStatusUpdateResponse& StatusUpdateResponse);
bool IsFinisherSplit(const FDTFluxSplitSensorResponse& SplitSensorResponse);
FDTFluxFinisher GetFinisherStatus(const FDTFluxSplitSensorResponse& SplitSensorResponse);
UFUNCTION(BlueprintCallable, Category="DTFlux|DataStorage")
bool GetContest(const int ContestId, FDTFluxContest& OutContest);
UFUNCTION(BlueprintCallable, Category="DTFlux|DataStorage")
TArray<FDTFluxStage> GetStages(const int ContestId);
UFUNCTION(BlueprintCallable, Category="DTFlux|DataStorage")
bool GetStage( const int ContestId, const int StageId, FDTFluxStage& OutStage);
UFUNCTION(BlueprintCallable, Category="DTFlux|DataStorage")
TArray<FDTFluxParticipant> GetParticipants(const int ContestId = -1);
UFUNCTION()
void GetParticipant(const int ContestID, const int ParticipantBib, FDTFluxParticipant& OutParticipant);
UFUNCTION(BlueprintCallable, Category="DTFlux|DataStorage")
TArray<FDTFluxStageRanking> GetStageRanking(const int ContestId, const int StageId);
UFUNCTION(BlueprintType, Category="DTFlux|Datastorage")
void AddOrUpdateContest(const FDTFluxContestResponse& ContestResponse);
UFUNCTION(BlueprintType, Category="DTFlux|Datastorage")
void AddOrUpdateParticipant(const FDTFluxTeamListItemResponse& TeamListItemResponse);
UFUNCTION(BlueprintType, Category="DTFlux|Datastorage")
void UpdateContestRanking(const FDTFluxContestRankingResponse& InContestRanking);
UFUNCTION(BlueprintType, Category="DTFlux|Datastorage")
void UpdateStageRanking(const FDTFluxStageRankingResponse& StageRankingResponse);
UFUNCTION(BlueprintCallable, Category="DTFlux")
bool IsInitialized()
{
return Contests.Num() < 0;
}
UFUNCTION(BlueprintCallable, Category="DTFlux|Datastorage")
bool GetParticipantByBib(const int Bib, FDTFluxParticipant& OutParticipant)
{
for(auto& Contest : Contests)
{
if(Contest.GetParticipant(Bib, OutParticipant))
{
return true;
}
}
return false;
}
UFUNCTION(BlueprintCallable, Category="DTFlux")
void AddSplitSensorResult(FDTFluxSplitSensorItemResponse Response);
UFUNCTION(BlueprintCallable, Category="DTFlux")
void ResetStageId(){ CurrentStageId = 0; }
UFUNCTION(BlueprintCallable, Category="DTFlux")
void SetCurrentStage(int NewId){ CurrentStageId = NewId; }
UFUNCTION(BlueprintCallable, Category="DTFlux")
void GoToNextStage();
UFUNCTION(BlueprintCallable, Category="DTFlux")
void ChangeCurrentContest();
UFUNCTION(BlueprintCallable, Category="DTFlux|DataStorage")
const FString GetConcurrentFormatedName( int Bib, bool Truncate = true, int MaxSize = 20);
UFUNCTION(BlueprintCallable, Category="DTFlux|DataStorage")
// ReSharper disable once IdentifierTypo
TArray<FDTFluxStageRanking> GetPoursuitWithStageTime(const TArray<int> ContestIds, const int StageId, float DelaTimeSeconds = 300.0f);
UFUNCTION(BlueprintCallable, Category="DTFlux|DataStorage")
// ReSharper disable once IdentifierTypo
TArray<FDTFluxStageRanking> GetPoursuitWithTimeStart(const TArray<int> ContestIds, const int StageId, float DelaTimeSeconds);
UFUNCTION()
bool GetFirstStageOfContest(const int ContestId, FDTFluxStage& Stage);
void DumpContest();
UFUNCTION()
bool GetSplit(const int ContestID, const int StageID, const int SplitID, FDTFluxSplit& OutSplit);
UFUNCTION()
FDTFluxSplitRanking AddSplitRanking(const FDTFluxSplitSensorItemResponse& SplitSensorItem);
UFUNCTION()
EDTFluxSplitType GetSplitStatus(int ContestID, int StageID, int SplitID);
bool GetStageRankingForBib(int ContestID, int StageID, int Bib, FDTFluxStageRanking& OutStageRanking);
};

View File

@ -0,0 +1,588 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "DTFluxAPILog.h"
#include "DTFluxModelResponse.h"
#include "DTFluxUtils/DTFluxEnums.h"
#include "UObject/Object.h"
#include "DTFluxModel.generated.h"
USTRUCT(BlueprintType, Category="DTFlux|Model")
struct DTFLUXAPI_API FDTFluxPerson
{
GENERATED_BODY()
public:
UPROPERTY(BlueprintReadWrite, Category="DTFlux|model")
FString FirstName;
UPROPERTY(BlueprintReadWrite, Category="DTFlux|model")
FString LastName;
UPROPERTY(BlueprintReadWrite, Category="DTFlux|model")
FString Gender;
UPROPERTY(BlueprintReadWrite, Category="DTFlux|model")
FString FunctionLine1 = TEXT("");
UPROPERTY(BlueprintReadWrite, Category="DTFlux|model")
FString FunctionLine2 = TEXT("");
};
USTRUCT(BlueprintType, Category="DTFlux|Model")
struct DTFLUXAPI_API FDTFluxParticipant
{
GENERATED_BODY()
public:
UPROPERTY(BlueprintReadWrite, Category="DTFlux|model")
int Bib = -1;
UPROPERTY(BlueprintReadWrite, Category="DTFlux|model")
FDTFluxPerson Person1;
UPROPERTY(BlueprintReadWrite, Category="DTFlux|model")
FDTFluxPerson Person2;
UPROPERTY(BlueprintReadWrite, Category="DTFlux|model")
FString Category;
UPROPERTY(BlueprintReadWrite, Category="DTFlux|model")
FString Club;
UPROPERTY(BlueprintReadWrite, Category="DTFlux|model")
bool Elite;
UPROPERTY(BlueprintReadWrite, Category="DTFlux|model")
TEnumAsByte<EDTFluxParticipantStatusType> Status;
UPROPERTY(BlueprintReadWrite, Category="DTFlux|model")
FString Team;
UPROPERTY(BlueprintReadWrite, Category="DTFlux|model")
int LastSplitId = 0;
bool IsTeam() const;
void Dump() const;
FString GetParticipantFormatedName(bool Truncate = false, int MaxSize = 20) const
{
FString ParticipantName;
if(Truncate)
{
if(IsTeam())
{
//Concatenate the team name;
if(Team.Len() > MaxSize - 3)
{
return Team.Left(MaxSize - 3).Append(TEXT("..."));
}
return Team;
}
if(Person1.FirstName.Contains("-") )
{
FString Formated = "";
//Compound Firstname
TArray<FString> Out;
Person1.FirstName.ParseIntoArray(Out,TEXT("-"),true);
for(const auto& Str : Out)
{
Formated.Append(Str.Left(1).ToUpper()).Append(".");
}
// TODO : Camel Case handling for LastName
Formated.Append(" ").Append(*Person1.LastName);
UE_LOG(LogDTFluxAPI, Log, TEXT("Firstname is with space compound. Formated Name %s length %02d MAX Size : %02d"),
*Formated, Formated.Len(), MaxSize);
if(Formated.Len() >= MaxSize)
{
UE_LOG(LogDTFluxAPI, Log, TEXT("Reducing %s Formated"), *Formated);
return Formated.Left(MaxSize - 3).Append("...");
}
return Formated;
}
if(Person1.FirstName.Contains(" "))
{
FString Formated = "";
//Compound Firstname
TArray<FString> Out;
Person1.FirstName.ParseIntoArray(Out,TEXT(" "),true);
for(const auto& Str : Out)
{
Formated.Append(Str.Left(1).ToUpper()).Append(".");
}
// TODO : Camel Case handling for LastName
Formated.Append(" ").Append(*Person1.LastName);
UE_LOG(LogDTFluxAPI, Log, TEXT("Firstname is with space compound. Formated Name %s length %02d MAX Size : %02d"),
*Formated, Formated.Len(), MaxSize);
if(Formated.Len() >= MaxSize)
{
UE_LOG(LogDTFluxAPI, Log, TEXT("Reducing %s Formated"), *Formated);
return Formated.Left(MaxSize - 3).Append("...");
}
return Formated;
}
FString Formated = Person1.FirstName.Left(1).Append(". ");
Formated.Append(Person1.LastName);
UE_LOG(LogDTFluxAPI, Log, TEXT("Firstname is not compound. Formated Name %s length %02d MAX Size : %02d"),
*Formated, Formated.Len(), MaxSize);
if(Formated.Len() >= MaxSize)
{
UE_LOG(LogDTFluxAPI, Log, TEXT("Reducing %s Formated"), *Formated);
return Formated.Left(MaxSize - 3).Append("...");
}
return Formated;
}
else
{
if(!IsTeam())
{
return FString::Printf(TEXT("%s %s"), *Person1.FirstName, *Person2.LastName);
}
return Team;
}
}
};
USTRUCT(BlueprintType, Category="DTFlux|Model")
struct DTFLUXAPI_API FDTFluxContestRanking
{
GENERATED_BODY()
public:
UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model")
int Bib;
UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model")
int Rank;
UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model")
FString Gap;
UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model")
FString Time;
void Dump () const
{
UE_LOG(LogDTFluxAPI, Log,
TEXT("FDTFluxContestRanking ->> \n \"rank\" : %d, Participant with Bib %d \"Gap\" : %s, \"Time\" : %s "),
Rank, Bib, *Gap, *Time );
};
};
USTRUCT(BlueprintType, Category="DTFlux|Model")
struct DTFLUXAPI_API FDTFluxStageRanking
{
GENERATED_BODY()
public:
UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model")
int Bib;
UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model")
int Rank;
UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model")
FString Gap;
UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model")
FString Time;
UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model")
FString TimeSwim;
UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model")
FString TimeTransition;
UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model")
FString TimeRun;
UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model")
FDateTime TimeStart;
UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model")
float SpeedRunning;
UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model")
float SpeedTotal;
UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model")
float SpeedSwim;
void Dump() const
{
UE_LOG(LogDTFluxAPI, Log, TEXT("RANKING : %02d. Participant bib %d %s %s %s %s %s"),
Rank, Bib, *Gap, *TimeSwim,
*TimeTransition, *TimeRun, *TimeStart.ToString());
}
};
USTRUCT(BlueprintType, Category="DTFlux|Model")
struct DTFLUXAPI_API FDTFluxSplitRanking
{
GENERATED_BODY()
public:
UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model")
int Bib;
UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model")
int ContestId = 0;
UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model")
int StageId = 0;
UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model")
int SplitId = 0;
UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model")
FString Gap;
UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model")
FString Time;
UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model")
int Rank = 0;
UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model")
bool Display = false;
void Dump() const
{
UE_LOG(LogDTFluxAPI, Log, TEXT("SplitGapItem"))
// Participant.Dump();
UE_LOG(LogDTFluxAPI, Log, TEXT("Bib %02d Rank %02d Gap %s Time %s"), Bib, Rank, *Gap, *Time);
}
};
USTRUCT(BlueprintType, Category="DTFlux|Model")
// ReSharper disable once IdentifierTypo
struct DTFLUXAPI_API FDTFluxFinisherData
{
GENERATED_BODY()
public:
UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model")
int ContestId;
UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model")
int StageId;
UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model")
int Bib = -1;
UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model")
FDTFluxSplitRanking SplitRanking;
UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model")
FDTFluxStageRanking StageRanking;
};
USTRUCT(BlueprintType, Category="DTFlux|Model")
struct DTFLUXAPI_API FDTFluxSplit
{
GENERATED_BODY()
public:
UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model")
int Id = -1;
UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model")
FString Name;
UPROPERTY(BlueprintReadWrite, Category="DTFlux|model")
TArray<FDTFluxSplitRanking> SplitRankings;
void Dump() const
{
UE_LOG(LogDTFluxAPI, Log, TEXT("Split %02d::%s *****\n"), Id, *Name);
for(const auto& SplitGapItem : SplitRankings)
{
SplitGapItem.Dump();
}
}
void InsertOrReplace(const FDTFluxStageRankingResponseItem& SplitRankingItemResp)
{
FDTFluxSplitRanking NewSplitGapItem;
NewSplitGapItem.Bib = SplitRankingItemResp.Bib;
NewSplitGapItem.Gap = SplitRankingItemResp.Gap;
NewSplitGapItem.Rank = SplitRankingItemResp.Rank;
NewSplitGapItem.Time = SplitRankingItemResp.Time;
if(SplitRankings.IsEmpty())
{
SplitRankings.Add(NewSplitGapItem);
return;
}
bool Update = true;
int Idx = 0;
for(auto& SplitGapItem : SplitRankings)
{
if(SplitGapItem.Bib == SplitRankingItemResp.Bib)
{
Update = false;
}
Idx++;
}
if(Update)
{
if(SplitRankings.IsValidIndex(Idx))
{
SplitRankings.RemoveAt(Idx);
}
}
SplitRankings.Add(NewSplitGapItem);
};
void SortByRank()
{
SplitRankings.Sort([](const FDTFluxSplitRanking& A, const FDTFluxSplitRanking& B)
{
if(A.Rank == 0 && B.Rank == 0)
return true;
return A.Rank < B.Rank;
});
}
TArray<FDTFluxSplitRanking> GetSplitRanking(const int From = 0, const int DisplayNumber = 0)
{
TArray<FDTFluxSplitRanking> NewSplitRankings;
SortByRank();
NewSplitRankings.Append(SplitRankings);
if(From == 0 && DisplayNumber == 0)
return NewSplitRankings;
for(auto& SRank : SplitRankings)
{
if(SRank.Rank >= From)
{
NewSplitRankings.Add(SRank);
if(NewSplitRankings.Num() >= DisplayNumber)
{
return NewSplitRankings;
}
}
}
return NewSplitRankings;
}
};
USTRUCT(BlueprintType, Category="DTFlux|Model")
struct DTFLUXAPI_API FDTFluxStage
{
GENERATED_BODY()
public:
UPROPERTY(BlueprintReadWrite, Category="DTFlux|model")
int Id;
UPROPERTY(BlueprintReadWrite, Category="DTFlux|model")
FString Name;
UPROPERTY(BlueprintReadWrite, Category="DTFlux|model")
FDateTime StartTime;
UPROPERTY(BlueprintReadWrite, Category="DTFlux|model")
FDateTime EndTime;
UPROPERTY(BlueprintReadWrite, Category="DTFlux|model")
FDateTime CutOff;
UPROPERTY(BlueprintReadWrite, Category="DTFlux|model")
TArray<FDTFluxSplit> Splits;
UPROPERTY(BlueprintReadWrite, Category="DTFlux|model")
TArray<FDTFluxStageRanking> StageRanking;
bool SetStartTime(const FDateTime& ContestDate, const FString& TimeString);
bool SetEndTime(const FDateTime& ContestDate, const FString& TimeString);
void Dump() const
{
UE_LOG(LogDTFluxAPI, Log, TEXT("Stage %02d::%s"), Id, *Name);
UE_LOG(LogDTFluxAPI, Log, TEXT("Starts at %s and is supposed to finnish at %s"),
*StartTime.ToString(), *EndTime.ToString());
UE_LOG(LogDTFluxAPI, Log, TEXT("Splits : \n"));
for(const auto& StageRankingEl : StageRanking)
{
StageRankingEl.Dump();
}
UE_LOG(LogDTFluxAPI, Log, TEXT("Splits : \n"));
for(const auto& Split : Splits)
{
Split.Dump();
}
}
EDTFluxSplitType GetSplitType(int SplitID)
{
int SplitCount = Splits.Num();
//sort by ID
Splits.Sort([](const FDTFluxSplit& A, const FDTFluxSplit& B)
{
return A.Id < B.Id;
});
int SplitIndex = Splits.IndexOfByPredicate([SplitID](const FDTFluxSplit& Split)
{
return Split.Id == SplitID;
});
if(SplitCount -2 == SplitIndex )
{
return EDTFluxSplitType::PreFinnishSplit;
}
if(SplitCount -1 == SplitIndex)
{
return EDTFluxSplitType::FinishSplit;
}
return EDTFluxSplitType::NormalSplit;
};
void SortStageRanking()
{
StageRanking.Sort([](const FDTFluxStageRanking& A, const FDTFluxStageRanking& B)
{
if(A.Rank == 0 || B.Rank == 0)
return true;
return A.Rank > B.Rank;
});
};
protected:
};
USTRUCT(BlueprintType, Category="DTFlux|Model")
struct DTFLUXAPI_API FDTFluxContest
{
GENERATED_BODY()
public:
UPROPERTY(BlueprintReadWrite, Category="DTFlux|model")
int Id = -1;
UPROPERTY(BlueprintReadWrite, Category="DTFlux|model")
FString Name;
UPROPERTY(BlueprintReadWrite, Category="DTFlux|model")
TArray<FDTFluxParticipant> Participants;
UPROPERTY(BlueprintReadWrite, Category="DTFlux|model")
TArray<FDTFluxStage> Stages;
UPROPERTY(BlueprintReadWrite, Category="DTFlux|model")
FDateTime Date;
UPROPERTY(BlueprintReadWrite, Category="DTFlux|model")
TArray<FDTFluxContestRanking> ContestRanking;
bool SetDate(const FString& StringDate);
void AddParticipant(const FDTFluxParticipant& Participant)
{
if(Participants.IsEmpty())
{
Participants.Add(Participant);
return;
}
int Index = 0;
FDTFluxParticipant ToUpdate;
bool Update = false;
for(auto P : Participants)
{
if(P.Bib == Participant.Bib)
{
UE_LOG(LogDTFluxAPI, Log, TEXT("Idx %d, OLD : %s %s new %s %s in Contest%02d"),
Index, *P.Person1.FirstName, *P.Person1.LastName,
*Participant.Person1.FirstName, *Participant.Person1.LastName, Id);
ToUpdate = P;
Update = true;
break;
}
else
{
Index++;
}
}
if(Update)
{
if(Participants.IsValidIndex(Index))
{
UE_LOG(LogDTFluxAPI, Log, TEXT("Idx %d, REMOVED : %s %s in Contest%02d"),
Index,
*Participants[Index].Person1.FirstName, *Participants[Index].Person1.LastName, Id);
Participants.RemoveAt(Index);
}
}
Participants.Add(Participant);
};
bool GetParticipant(const int Bib, FDTFluxParticipant& OutParticipant)
{
for (auto& Participant : Participants)
{
if(Participant.Bib == Bib)
{
OutParticipant = Participant;
return true;
}
}
return false;
}
void DumpParticipant()
{
int Num = 0;
for(const auto& Participant: Participants )
{
UE_LOG(LogDTFluxAPI, Log, TEXT("DUMP Participant : Name -> %s Bib %d"), *Participant.Person1.FirstName, Participant.Bib);
Num ++;
}
UE_LOG(LogDTFluxAPI, Log, TEXT("DUMP Participant : In Contest with ID %d there are %d Participant(s)"), Id, Num);
};
bool AddContestRanking(const FDTFluxContestRanking& NewRanking)
{
bool Update = false;
if(ContestRanking.IsEmpty())
{
ContestRanking.Add(NewRanking);
return true;
}
int Idx = 0;
for( auto& Ranking : ContestRanking)
{
if(NewRanking.Bib == Ranking.Bib)
{
// we need to update a ranking
Update = true;
break;
}
Idx++;
}
if(Update)
{
ContestRanking.RemoveAt(Idx);
ContestRanking.Insert(NewRanking, Idx);
UE_LOG(LogDTFluxAPI, Log,
TEXT("Inserting %d with rank %d in Contest with ID %d"),
NewRanking.Bib, NewRanking.Rank, Id );
return true;
}
ContestRanking.Add(NewRanking);
return true;
}
void Dump()
{
UE_LOG(LogDTFluxAPI, Log, TEXT("CONTEST DUMP BEGIN *****%s::%02d *****\n"), *Name, Id);
UE_LOG(LogDTFluxAPI, Log, TEXT("Date : %s"), *Date.ToString());
UE_LOG(LogDTFluxAPI, Log, TEXT("PARTICIPANTS : \n"));
DumpParticipant();
for(auto& Stage: Stages)
{
Stage.Dump();
}
for(auto& ContestRankingEl: ContestRanking)
{
ContestRankingEl.Dump();
}
UE_LOG(LogDTFluxAPI, Log, TEXT("CONTEST DUMP END *****%s::%02d *****\n"), *Name, Id);
}
void SortContestRanking()
{
ContestRanking.Sort([](const FDTFluxContestRanking& A, const FDTFluxContestRanking& B)
{
if(A.Rank == 0 || B.Rank == 0)
return true;
return A.Rank > B.Rank;
});
};
};
USTRUCT(BlueprintType, Category="FDTFlux|Model")
struct DTFLUXAPI_API FDTFluxFinisher
{
GENERATED_BODY()
UPROPERTY(BlueprintReadOnly, Category="FDTFlux|Model")
TEnumAsByte<EDTFluxFinisherType> Type;
UPROPERTY(BlueprintReadOnly, Category="FDTFlux|Model")
FDTFluxParticipant Participant;
UPROPERTY(BlueprintReadOnly, Category="FDTFlux|Model")
FDTFluxStageRanking CurrentRanking;
};
USTRUCT(BlueprintType, Category="DTFlux|Subsystem|Events")
struct DTFLUXAPI_API FDTFluxStageFinished
{
GENERATED_BODY()
UPROPERTY(BlueprintReadOnly, Category="DTFlux|Subsystem|Events")
int ContestId = 0;
UPROPERTY(BlueprintReadOnly, Category="DTFlux|Subsystem|Events")
int StageId = 0;
UPROPERTY(BlueprintReadOnly, Category="DTFlux|Subsystem|Events")
TArray<FDTFluxStageRanking> Rankings;
};
USTRUCT(BlueprintType, Category="DTFlux|Subsystem|Events")
struct DTFLUXAPI_API FDTFluxContestFinished
{
GENERATED_BODY()
UPROPERTY(BlueprintReadOnly, Category="DTFlux|Subsystem|Events")
int ContestId = 0;
UPROPERTY(BlueprintReadOnly, Category="DTFlux|Subsystem|Events")
TArray<FDTFluxStageRanking> Rankings;
};
USTRUCT(BlueprintType, Category="DTFlux|Subsystem")
struct FDTFluxPoursuit
{
GENERATED_BODY()
UPROPERTY(BlueprintReadOnly, Category="DTFlux|Subsystem")
int Bib;
UPROPERTY(BlueprintReadOnly, Category="DTFlux|Subsystem")
FDateTime TimeStart;
};

View File

@ -0,0 +1,321 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "UObject/Object.h"
#include "DTFluxUtils/DTFluxEnums.h"
#include "DTFluxModelResponse.generated.h"
/**
*
*/
USTRUCT(BlueprintType, Category="DTFlux|Model")
struct DTFLUXAPI_API FSplitResponse
{
GENERATED_BODY()
public:
UPROPERTY()
FString Type = "split-response-data";
UPROPERTY()
int Id;
UPROPERTY()
FString Name;
};
USTRUCT(BlueprintType, Category="DTFlux|Model")
struct DTFLUXAPI_API FStageResponse
{
GENERATED_BODY()
public:
UPROPERTY()
FString Type = "stage-response-data";
UPROPERTY()
int Id;
UPROPERTY()
FString Name;
UPROPERTY()
FString StartTime;
UPROPERTY()
FString EndTime;
UPROPERTY()
FString CutOff;
};
USTRUCT(BlueprintType, Category="DTFlux|Model")
struct DTFLUXAPI_API FDTFluxContestResponse
{
GENERATED_BODY()
public:
UPROPERTY()
FString Type = "contest";
UPROPERTY()
int Id;
UPROPERTY()
FString Name;
UPROPERTY()
FDateTime Date;
UPROPERTY()
TArray<FStageResponse> Stages;
UPROPERTY()
TArray<FSplitResponse> Splits;
};
USTRUCT()
struct DTFLUXAPI_API FDTFluxContestRankingResponseItem
{
GENERATED_BODY()
public:
UPROPERTY()
FString Type = "team-contest-ranking";
UPROPERTY()
int Bib;
UPROPERTY()
int Rank;
UPROPERTY()
FString Time;
UPROPERTY();
FString Gap;
UPROPERTY();
FString SpeedSwimAverage;
UPROPERTY();
FString SpeedRunningAverage;
UPROPERTY();
FString SpeedTotalAverage;
};
USTRUCT()
struct DTFLUXAPI_API FDTFluxContestRankingResponse
{
GENERATED_BODY()
public:
UPROPERTY()
FString Type = "contest-ranking";
UPROPERTY()
int ContestID;
UPROPERTY()
TArray<FDTFluxContestRankingResponseItem> Datas;
};
USTRUCT()
struct DTFLUXAPI_API FDTFluxStageRankingResponseItem
{
GENERATED_BODY()
public:
UPROPERTY()
FString Type = "team-stage-ranking";
UPROPERTY()
int Bib;
UPROPERTY()
int Rank;
UPROPERTY()
FString Time;
UPROPERTY();
FString Gap;
UPROPERTY()
FString TimeSwim;
UPROPERTY();
FString TimeTransition;
UPROPERTY()
FString TimeRun;
UPROPERTY();
FString TimeStart;
UPROPERTY()
float SpeedSwim;
UPROPERTY()
float SpeedRunning;
UPROPERTY()
float SpeedTotal;
};
USTRUCT()
struct DTFLUXAPI_API FDTFluxStageRankingResponse
{
GENERATED_BODY()
public:
UPROPERTY()
FString Type = "stage-ranking";
UPROPERTY()
int ContestID;
UPROPERTY()
int StageID;
UPROPERTY()
int SplitID = -1;
UPROPERTY()
// ReSharper disable once IdentifierTypo
TArray<FDTFluxStageRankingResponseItem> Datas;
};
USTRUCT(BlueprintType, Category="DTFlux|Model")
struct DTFLUXAPI_API FDTFluxRaceDataResponse
{
GENERATED_BODY()
public:
UPROPERTY()
// ReSharper disable once StringLiteralTypo
FString Type = "race-datas";
UPROPERTY()
// ReSharper disable once IdentifierTypo
TArray<FDTFluxContestResponse> Datas;
};
USTRUCT()
struct DTFLUXAPI_API FDTFluxStatusTeamUpdateResponse
{
GENERATED_BODY()
UPROPERTY()
FString Type = "status-team-update";
UPROPERTY()
int Bib;
UPROPERTY()
int Status;
};
USTRUCT()
struct DTFLUXAPI_API FDTFluxStatusUpdateResponse
{
GENERATED_BODY()
UPROPERTY()
FString Type = "status-update";
UPROPERTY()
// ReSharper disable once IdentifierTypo
FDTFluxStatusTeamUpdateResponse Datas;
};
USTRUCT(BlueprintType, Category="DTFlux|Model")
struct DTFLUXAPI_API FDTFluxSplitSensorItemResponse
{
GENERATED_BODY()
UPROPERTY()
int Bib;
UPROPERTY()
FString Type = "split-sensor-item";
UPROPERTY()
int ContestID;
UPROPERTY()
int StageID;
UPROPERTY()
int SplitID;
UPROPERTY()
FString Time = "-";
UPROPERTY()
FString Gap = "-";
UPROPERTY()
int Rank;
};
USTRUCT(BlueprintType, Category="DTFlux|Model")
struct DTFLUXAPI_API FDTFluxSplitSensorResponse
{
GENERATED_BODY()
UPROPERTY()
FString Type = "split-sensor";
UPROPERTY()
// ReSharper disable once IdentifierTypo
TArray<FDTFluxSplitSensorItemResponse> Datas;
};
USTRUCT(BlueprintType, Category="DTFlux|Model")
struct DTFLUXAPI_API FDTFluxTeamListItemResponse
{
GENERATED_BODY()
public:
UPROPERTY()
FString Type = "team-list-item";
UPROPERTY()
int ContestId;
UPROPERTY()
int Bib;
UPROPERTY()
FString FirstName;
UPROPERTY()
FString LastName;
UPROPERTY()
FString FirstName2 = "";
UPROPERTY()
FString LastName2 = "";
UPROPERTY()
FString Team = "";
UPROPERTY()
FString Gender;
UPROPERTY()
FString Gender2;
UPROPERTY()
bool Elite;
UPROPERTY()
FString Category;
UPROPERTY()
int Status;
UPROPERTY()
FString Club;
};
USTRUCT(BlueprintType, Category="DTFlux|Model")
struct DTFLUXAPI_API FDTFluxTeamListResponse
{
GENERATED_BODY()
public:
UPROPERTY()
FString Type = "team-list";
UPROPERTY()
// ReSharper disable once IdentifierTypo
TArray<FDTFluxTeamListItemResponse> Datas;
};
USTRUCT()
struct DTFLUXAPI_API FDTFluxTeamUpdateResponse
{
GENERATED_BODY()
UPROPERTY()
FString Type = "team-update";
UPROPERTY()
// ReSharper disable once IdentifierTypo
TArray<FDTFluxTeamListItemResponse> Datas;
};
USTRUCT()
struct DTFLUXAPI_API FDTFluxArchSelectResponseItem
{
GENERATED_BODY()
UPROPERTY()
FString Type = "arch-select";
UPROPERTY()
int ContestId;
UPROPERTY()
int StageId;
};
USTRUCT()
struct DTFLUXAPI_API FDTFluxArchSelectResponse
{
GENERATED_BODY()
UPROPERTY()
FString Type = "arch-select";
UPROPERTY()
TArray<FDTFluxArchSelectResponseItem> Datas;
};
USTRUCT(BlueprintType, Category="DTFlux|Subsystem|Events")
struct DTFLUXAPI_API FDTFluxWsResponseEvent
{
GENERATED_BODY()
UPROPERTY(BlueprintReadOnly, Category="DTFlux|Subsystem|Events")
TEnumAsByte<EDTFluxResponseType> WsResponseType;
UPROPERTY(BlueprintReadOnly, Category="DTFlux|Subsystem|Events")
FString RawData;
};

View File

@ -0,0 +1,92 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Engine/DeveloperSettings.h"
#include "DTFluxAPILog.h"
#include "DTFluxProjectSettings.generated.h"
DECLARE_MULTICAST_DELEGATE_TwoParams(OnDTFluxProjectSettingsModified, FString, const UDTFluxProjectSettings* );
/**
* DTFlux project settings
*/
UCLASS(Blueprintable, Config=Engine, DefaultConfig, meta=(DisplayName="DTFlux Project Settings"))
class DTFLUXAPI_API UDTFluxProjectSettings : public UDeveloperSettings
{
GENERATED_BODY()
public:
OnDTFluxProjectSettingsModified OnProjectSettingsModified;
// UPROPERTY(Category="DTFlux|Config|Race Result API", Config, EditAnywhere, BlueprintReadOnly)
// int RaceResultPort = 80;
//
// UPROPERTY(Category="DTFlux|Config|Race Result API", Config, EditAnywhere, BlueprintReadOnly)
// FString RaceResultUrl = "http://localhost";
//
// UPROPERTY(Category="DTFlux|Config|Race Result API", Config, EditAnywhere, BlueprintReadOnly)
// FString RaceResultSessionID;
//
// UPROPERTY(Category="DTFlux|Config|Race Result API", Config, EditAnywhere, BlueprintReadOnly)
// bool bAccessIsLocal = true;
//
// UPROPERTY(Category="DTFlux|Config|Race Result API", Config, EditAnywhere, BlueprintReadOnly)
// FString StartListAccessToken;
//
// UPROPERTY(Category="DTFlux|Config|Race Result API", Config, EditAnywhere, BlueprintReadOnly)
// FString GeneralClassificationAccessToken;
//
// UPROPERTY(Category="DTFlux|Config|Race Result API", Config, EditAnywhere, BlueprintReadOnly)
// FString LiveStageResultsAccessToken ;
// Proxy
UPROPERTY(Category="DTFlux|Config|Chrono Proxy", Config, EditAnywhere, BlueprintReadOnly)
int WebsocketServerPort = 3000;
UPROPERTY(Category="DTFlux|Config|Chrono Proxy", Config, EditAnywhere, BlueprintReadOnly)
FString WebsocketServerAddress = "ws://127.0.0.1/ws";
UPROPERTY(Category="DTFlux|Config|Chrono Proxy", Config, EditAnywhere, BlueprintReadOnly)
FString ProxyAddress = "http://localhost";
// UPROPERTY(Category="DTFlux|Chrono Proxy", Config, EditAnywhere, BlueprintReadOnly)
// int ProxyPort = 8000;
//
// UPROPERTY(Category="DTFlux|Config|Chrono Proxy", Config, EditAnywhere, BlueprintReadOnly)
// FString ProxyRootPath = "/endpoints";
// UPROPERTY(Category="DTFlux|Config|Chrono Proxy", Config, EditAnywhere, BlueprintReadOnly)
// FString ProxyRaceDataEndpoint;
// UPROPERTY(Category="DTFlux|Config|Chrono Proxy", Config, EditAnywhere, BlueprintReadOnly)
// FString ProxyRankingEndpoint;
// UPROPERTY(Category="DTFlux|Config|Chrono Proxy", Config, EditAnywhere, BlueprintReadOnly)
// FString ProxyTeamsEndpoint;
//Server Config ****NOT USED****
UPROPERTY(Category="DTFlux|Server Config", Config, EditAnywhere, BlueprintReadOnly)
int InPort = 8080;
UFUNCTION(BlueprintCallable, Category="DTFlux|Config")
static const UDTFluxProjectSettings* GetDTFluxAPIProjectSettings();
#if WITH_EDITOR
virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override;
#endif
protected:
private:
UDTFluxProjectSettings();
};

View File

@ -0,0 +1,193 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Runtime/Engine/Public/Subsystems/EngineSubsystem.h"
// #include "DTFluxWebSocket/DTFluxWebsocketServer.h"
//
// #include "HttpServerRequest.h"
// #include "HttpResultCallback.h"
// #include "HttpRouteHandle.h"
// #include <string>
#include "DTFluxAPILog.h"
#include "DTFluxDataStorage/DTFluxDataStorage.h"
#include "DTFluxModel/DTFluxModel.h"
#include "DTFluxSubsystemAPISettings/DTFluxSubsystemAPISettings.h"
#include "DTFluxWebSocket/DTFluxWebsocketClient.h"
#include "DTFluxSubsystem.generated.h"
class UDTFluxDataStorage;
class UDTFluxProjectSettings;
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnTimerTriggered);
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnRaceDataReceived);
DECLARE_DYNAMIC_DELEGATE_OneParam(FOnTimer, FString, TimerName);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnWsEvent, FDTFluxWsResponseEvent, WsResponseEvent);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnFinisher, FDTFluxFinisherData, FinisherData);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnSpotter, FDTFluxFinisherData, SpotterData);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnSplitSensor, FDTFluxSplitRanking, ParticipantSplitData);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnContestBegin, int, ContestId);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnStageBegin, int, ContestId, int, StageId);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnTimesUp, int, ContestId, int, StageId);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnRestTimeBegin, int, ContestId, int, StageId);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnArchSelect, int, ContestId, int, StageId);
/**
* DTFlux API Subsystem
*
* This Subsystem set up a Websocket server and Listen to incoming events and poll some http request to Proxy when needed.
* it handles a datastore where data are being saved and present to blueprint or actors.
*/
UCLASS(BlueprintType, Category="DTFlux|API Subsystem")
class DTFLUXAPI_API UDTFluxSubsystem : public UEngineSubsystem, public FTickableGameObject
{
GENERATED_BODY()
private:
static const UDTFluxProjectSettings* GetSettings();
int WebSocketPort = 0;
FDTFluxSubsystemAPISettings SubSettings;
UPROPERTY()
UDTFluxWebSocketClient* WsClient;
UPROPERTY()
UDTFluxDataStorage* DataStorage;
virtual void Tick(float DeltaTime) override;
virtual bool IsTickableInEditor() const override
{
return true;
}
virtual TStatId GetStatId() const override
{
RETURN_QUICK_DECLARE_CYCLE_STAT(UDTFluxSubsystem, STATGROUP_Tickables);
}
protected:
UFUNCTION()
void RequestRaceDatas();
UFUNCTION()
void RequestTeamList();
UFUNCTION()
void RequestContestRanking(const int ContestId);
UFUNCTION()
void RequestStageRanking(const int ContestId, const int StageId);
UFUNCTION()
void RequestSplitGaps(const int ContestId, const int StageId, const int SplitId);
UPROPERTY()
FDateTime TestTimer;
UFUNCTION()
void BroadcastTimerEvent();
UPROPERTY()
TMap<FDateTime, FOnTimer> Timer;
bool DataStorageRaceDataInit = false;
bool DataStorageTeamListInit = false;
public:
/** Implement this for initialization of instances of the system */
virtual void Initialize(FSubsystemCollectionBase& Collection) override;
/** Implement this for deinitialization of instances of the system */
virtual void Deinitialize() override;
void InitDataStorage();
void LoadConfig(const UDTFluxProjectSettings* Settings);
UFUNCTION(BlueprintCallable, Category="DTFluxAPI | Subsytem")
bool ReloadSubsystem();
UPROPERTY(BlueprintAssignable, Category="DTFlux|Events")
FOnTimerTriggered OnTimerTriggered;
UPROPERTY(BlueprintAssignable, Category="DTFlux|Events")
FOnWsEvent OnWsEvent;
UPROPERTY(BlueprintAssignable, Category="DTFlux|Events")
FOnSplitSensor OnSplitSensor;
UPROPERTY(BlueprintAssignable, Category="DTFlux|Events")
FOnSpotter OnSpotter;
UPROPERTY(BlueprintAssignable, Category="DTFlux|Events")
FOnFinisher OnFinisher;
UPROPERTY(BlueprintAssignable, Category="DTFlux|Events")
FOnContestBegin OnContestBegin;
UPROPERTY(BlueprintAssignable, Category="DTFlux|Events")
FOnStageBegin OnStageBegin;
UPROPERTY(BlueprintAssignable, Category="DTFlux|Events")
FOnTimesUp OnTimesUp;
UPROPERTY(BlueprintAssignable, Category="DTFlux|Events")
FOnRestTimeBegin FOnRestTimeBegin;
UPROPERTY(BlueprintAssignable, Category="DTFlux|Events")
FOnRaceDataReceived OnRaceDataReceived;
UPROPERTY(BlueprintAssignable, Category="DTFlux|Events")
FOnArchSelect OnArchSelect;
// UPROPERTY(BlueprintReadWrite, Category="DTFlux|Subsystem|Websocket")
// int ReconnectTimeout = 60; //seconds
UFUNCTION(BlueprintCallable, Category="DTFlux|Subsystem|WebSocket")
bool Reconnect();
UFUNCTION(BlueprintCallable, Category="DTFlux|Subsystem|WebSocket")
bool AddTimer(FDateTime Time, FOnTimer NewTimer);
UFUNCTION(BlueprintCallable, Category="DTFlux|Subsystem")
void SetTimerEvent(const FDateTime& When);
UFUNCTION(BlueprintCallable, Category="DTFlux|Subsystem")
void UpdateRaceData();
UFUNCTION(BlueprintCallable, Category="DTFlux|Subsystem")
void UpdateTeamList();
UFUNCTION(BlueprintCallable, Category="DTFlux|Subsystem")
void UpdateTeam();
UFUNCTION(BlueprintCallable, Category="DTFlux|Subsystem")
void UpdateContestRanking(const int ContestID);
UFUNCTION(BlueprintCallable, Category="DTFlux|Subsystem")
void UpdateStageRanking(const int ContestID, const int StageID, const int SplitID = -1);
UFUNCTION(BlueprintCallable, Category="DTFlux|Subsystem")
UDTFluxDataStorage* GetDataStorage()
{
return DataStorage;
};
UFUNCTION()
void ProcessTeamListResponse(const FDTFluxTeamListResponse& TeamListResponse);
UFUNCTION()
void ProcessRaceDataResponse(const FDTFluxRaceDataResponse& DataResponse);
UFUNCTION()
void ProcessContestRankingResponse(const FDTFluxContestRankingResponse& ContestRankingResponse);
UFUNCTION()
void ProcessStageRankingResponse(const FDTFluxStageRankingResponse& StageRankingResponse);
UFUNCTION()
void ProcessSplitRankingResponse(const FDTFluxStageRankingResponse& StageRankingResponse);
UFUNCTION()
void ProcessTeamUpdateResponse(const FDTFluxTeamUpdateResponse& TeamUpdateResponse);
UFUNCTION()
void ProcessStatusUpdateResponse(const FDTFluxStatusUpdateResponse& StatusUpdateResponse);
UFUNCTION()
void ProcessSplitSensor(const FDTFluxSplitSensorResponse& SplitSensorResponse);
UFUNCTION()
void ProcessArchSelect(FDTFluxArchSelectResponseItem ArchSelectResponse);
UFUNCTION()
void WsConnected();
UFUNCTION()
void WsReceivedMessage(const FString& MessageReceived);
UFUNCTION()
void WsConnectionClosed(const FString& Reason);
UFUNCTION()
void WsConnectionError(const FString& Error);
UFUNCTION(BlueprintCallable, Category="DTFlux|subsystem")
bool IsConnected() const;
UFUNCTION(BlueprintCallable, Category="DTFlux|Subsystem")
TArray<FDTFluxSplitRanking> SortByRank(TArray<FDTFluxSplitRanking> SplitRankingArray);
};

View File

@ -0,0 +1,102 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "DTFluxUtils/DTFluxEnums.h"
#include "Subsystems/WorldSubsystem.h"
#include "DTFluxSubsystemTimer.generated.h"
/**
*
*/
class UDTFluxDataStorage;
class UDTFluxSubsystem;
USTRUCT()
struct FDTFluxContestTimerHandle
{
GENERATED_BODY()
public:
UPROPERTY()
int ContestId;
UPROPERTY()
int StageId;
UPROPERTY();
TEnumAsByte<EDTFluxTimerEventType> Type;
UPROPERTY();
FTimerHandle Handle;
};
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnCutoff, TArray<int>, ContestIds, int, StageId);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnStageStarted, const TArray<int>&, ContestIds, int, StageId);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FOnStageLoading, const TArray<int>&, ContestIds, int, StageId, int, DelayBeforeStageStart);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnRemoveLineRequested, const TArray<int>&, LineIndex);
UCLASS(BlueprintType, Category="DTFlux|Timer")
class DTFLUXAPI_API UDTFluxSubsystemTimer : public UTickableWorldSubsystem
{
GENERATED_BODY()
public:
/** Implement this for initialization of instances of the system */
virtual void Initialize(FSubsystemCollectionBase& Collection) override;
/** Implement this for deinitialization of instances of the system */
virtual void Deinitialize() override;
virtual void Tick(float DeltaTime) override;
virtual TStatId GetStatId() const override
{
RETURN_QUICK_DECLARE_CYCLE_STAT(UDTFluxSubsystemTimer, STATGROUP_Tickables);
}
UPROPERTY()
TArray<FDTFluxContestTimerHandle> Timers;
UPROPERTY(BlueprintAssignable, Category="DTFlux|Timer")
FOnCutoff OnCutoff;
UPROPERTY(BlueprintAssignable, Category="DTFlux|Timer")
FOnStageStarted OnStageStarted;
UPROPERTY(BlueprintAssignable, Category="DTFlux|Timer")
FOnStageLoading OnStageLoading;
UPROPERTY(BlueprintAssignable, Category="DTFlux|Timer")
FOnRemoveLineRequested OnRemoveLineRequested;
UFUNCTION()
void OnDataStorageInit();
void AddCutoffTimer(const int ContestID, const int StageID);
void AddStageStartedTimer(const int ContestID, const int StageID);
void OnStartTimer();
void OnCutOffTimer();
void ClearTimer(FDTFluxContestTimerHandle TimerHandle);
void ClearTimer(const int HandleIndex);
UFUNCTION(BlueprintCallable, Category="DTFlux|Timer")
void TriggerOnCutOff(const TArray<int>& ContestIds, const int StageId);
UFUNCTION(BlueprintCallable, Category="DTFlux|Timer")
void TriggerStartTime(const TArray<int>& ContestIds, const int StageId);
UFUNCTION(BlueprintCallable, Category="DTFlux|Timer")
void TriggerStageLoading(const TArray<int>& ContestIds, int StageId, int DelayBeforeStageStart);
UFUNCTION(BlueprintCallable, Category="DTFlux|Timer")
void TriggerOnDeleteRequested( const TArray<int>& LineIndex);
static UDTFluxSubsystem* GetDTFluxSubSystem();
static UDTFluxDataStorage* GetDTFluxDataStorage();
static float GetSecondsFrom(const FDateTime When);
};

View File

@ -0,0 +1,35 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "UObject/Object.h"
#include "DTFluxSubsystemAPISettings.generated.h"
/**
*
*/
USTRUCT()
struct FDTFluxSubsystemAPISettings
{
GENERATED_BODY()
public:
FString WebsocketAddress = "ws://localhost";
int WebsocketPort = 3000;
FString ProxyAddress = "http://localhost";
int ProxyPort = 80;
//TODO : Maybe we must make a dedicated struct with enum to make endpoints more clean.
TMap<FString, FString> ProxyEndpoints;
static FString GetRaceDataEndpoint(const FDTFluxSubsystemAPISettings* Settings);
static FString GetContestRankingEndpoint(const FDTFluxSubsystemAPISettings* Settings, const int ContestId);
static FString GetStageRankingEndpoint(const FDTFluxSubsystemAPISettings* Settings, const int ContestId, const int StageId);
static FString GetStageRankingFilteredEndpoint(const FDTFluxSubsystemAPISettings* Settings, const int ContestId, const int StageId, const FString SplitName);
static FString GetTeamsEndpoint(const FDTFluxSubsystemAPISettings* Settings);
private:
FString GetProxyBaseEndpoint() const
{
return FString::Printf(TEXT("%s:%i"), *ProxyAddress, ProxyPort);
};
};

View File

@ -0,0 +1,103 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "UObject/Object.h"
#include "DTFluxEnums.generated.h"
/**
*
*/
UENUM()
enum EDTFluxParticipantStatusType : uint8
{
Normal = 0 UMETA(DisplayName="Normal"),
OutOfRace = 1 UMETA(DisplayName="HorsCourse"),
DSQ = 2 UMETA(DisplayName="Disqualifié"),
DNF = 3 UMETA(DisplayName="Abandon"),
DNS = 4 UMETA(DisplayName="NonPartant"),
NotLinedUp = 5 UMETA(DisplayName="NonPresentAuDépart"),
};
UENUM()
enum EDTFluxFinisherType : uint8
{
Winner = 0 UMETA(DisplayName="Winner"),
Spotter = 1 UMETA(DisplayName="Spotter"),
Finish = 2 UMETA(DisplayName="Finish"),
};
UENUM(BlueprintType, Category="DTFlux|Server")
enum EDTFluxResponseErrorCode
{
Unknown_Error UMETA(DisplayName="Unknown Error"),
InvalidBody_Error UMETA(DisplayName="Invalid Body"),
InvalidRequest_Error UMETA(DisplayName="Invalid Request"),
Internal_Error UMETA(DisplayName="Internal Server Error")
};
UENUM(BlueprintType, Category="DTFlux|Subsystem")
enum EDTFluxResponseType: uint8
{
UnknownResponse = 0 UMETA(DisplayName="UnknownResponse"),
RaceData = 1 UMETA(DisplayName="RaceData"),
ContestRanking = 2 UMETA(DisplayName="ContestRanking"),
StageRanking = 3 UMETA(DisplayName="StageRanking"),
SplitRanking = 4 UMETA(DisplayName="SplitRanking"),
TeamList = 5 UMETA(DisplayName="TeamList"),
TeamUpdate = 6 UMETA(DisplayName="TeamUpdate"),
SplitSensor = 7 UMETA(DisplayName="SplitSensor"),
StatusUpdate = 8 UMETA(DisplayName="StatusUpdate"),
WsConnected = 9 UMETA(DisplayName="WsConnected"),
WsClosed = 10 UMETA(DisplayName="WsClosed"),
WsError = 11 UMETA(DisplayName="WsError"),
ArchSelect = 12 UMETA(DisplayName="ArchSelect"),
};
UENUM(BlueprintType, Category="DTFlux|Subsystem")
enum EDTFluxSplitType : uint8
{
UnknownSplitType = 0 UMETA(DisplayName="UnknownSplitType"),
NormalSplit = 1 UMETA(DisplayName="NormalSplit"),
PreFinnishSplit = 2 UMETA(DisplayName="PreFinnishSplit"),
FinishSplit = 3 UMETA(DisplayName="FinishSplit"),
};
UENUM(BlueprintType, Category="DTFlux|DataStorage")
// ReSharper disable once IdentifierTypo
enum EDTFluxDataStorageEventType : uint8
{
UnknownEvent = 0 UMETA(DisplayName="ParticipantUpdateEvent"),
ParticipantCreateEvent = 1 UMETA(DisplayName="ParticipantUpdateEvent"),
ParticipantUpdateEvent = 2 UMETA(DisplayName="ParticipantUpdateEvent"),
ParticipantDeleteEvent = 3 UMETA(DisplayName="ParticipantDeleteEvent"),
ParticipantStatusUpdateEvent = 4 UMETA(DisplayName="ParticipantUpdateEvent"),
RaceDataCreateEvent = 5 UMETA(DisplayName="RaceDataCreateEvent"),
RaceDataUpdateEvent = 6 UMETA(DisplayName="RaceDataUpdateEvent"),
RaceDataDeleteEvent = 7 UMETA(DisplayName="RaceDataDeleteEvent"),
ContestRankingUpdate = 8 UMETA(DisplayName="ContestRankingUpdate"),
StageRankingUpdate = 9 UMETA(DisplayName="StageRankingUpdate"),
SplitRankingUpdate = 10 UMETA(DisplayName="SplitRankingUpdate"),
};
UENUM()
enum EDTFluxTimerEventType : uint8
{
StageStart = 0 UMETA(DisplayName="StageStart"),
StageCutOff = 1 UMETA(DisplayName="StageCutOff"),
};
UENUM()
enum EDTFluxStageStatusType : uint8
{
UnknownStatus = 0 UMETA(DisplayName="UnknownStatus"),
StageWaiting = 1 UMETA(DisplayName="StageWaiting"),
StageStarted = 2 UMETA(DisplayName="StageStarted"),
StageEnded = 3 UMETA(DisplayName="StageCutOff")
};

View File

@ -0,0 +1,135 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "CrossCompilerCommon.h"
#include "DTFluxModel/DTFluxModel.h"
#include "DTFluxSubsystem/DTFluxSubsystem.h"
#include "UObject/Object.h"
#include "DTFluxUtils.generated.h"
/**
*
*/
UCLASS(BlueprintType, Category="DTFlux|Model|Helpers")
class DTFLUXAPI_API UDTFluxModelHelper : public UBlueprintFunctionLibrary
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintCallable, Category="DTFlux|Model")
static bool IsParticipantATeam(const FDTFluxParticipant& Participant)
{
return Participant.Person2.FirstName != "";
}
UFUNCTION(BlueprintCallable, Category="DTFlux|Model|Helpers")
static TArray<FDTFluxSplitRanking> GetSplitRanking(const int ContestId, const int StageId,
const int SplitId, const int From = 0, const int DisplayNumber = 0)
{
TArray<FDTFluxSplitRanking> SplitRankings;
UDTFluxSubsystem* Subsystem = GEngine->GetEngineSubsystem<UDTFluxSubsystem>();
TArray<FDTFluxContest> Contests = Subsystem->GetDataStorage()->Contests;
for( auto& Contest : Contests)
{
if(Contest.Id == ContestId)
{
for( auto& Stage : Contest.Stages)
{
if(Stage.Id == StageId)
{
for( auto& Split : Stage.Splits)
{
if(Split.Id == SplitId)
{
Split.SortByRank();
return Split.GetSplitRanking(From, DisplayNumber);
}
}
}
}
}
}
return SplitRankings;
}
UFUNCTION(BlueprintCallable, Category="DTFlux|Model|Helpers")
static TArray<FDTFluxStageRanking> GetStageRanking(const int ContestId, const int StageId, const int From = 0, const int DisplayNumber = 0)
{
TArray<FDTFluxStageRanking> StageRankings;
UDTFluxSubsystem* Subsystem = GEngine->GetEngineSubsystem<UDTFluxSubsystem>();
TArray<FDTFluxContest> Contests = Subsystem->GetDataStorage()->Contests;
for( auto& Contest : Contests)
{
if(Contest.Id == ContestId)
{
for( auto& Stage : Contest.Stages)
{
if(Stage.Id == StageId)
{
StageRankings = Stage.StageRanking;
}
}
}
}
//CAREFUL Can Be Empty
return StageRankings;
}
UFUNCTION(BlueprintCallable, Category="DTFlux|Model|Helpers")
static TArray<FDTFluxContestRanking> GetContestRanking(const int ContestId, const int StageId, const int From = 0, const int DisplayNumber = 0)
{
TArray<FDTFluxContestRanking> ContestRankings;
UDTFluxSubsystem* Subsystem = GEngine->GetEngineSubsystem<UDTFluxSubsystem>();
TArray<FDTFluxContest> Contests = Subsystem->GetDataStorage()->Contests;
for( auto& Contest : Contests)
{
if(Contest.Id == ContestId)
{
ContestRankings = Contest.ContestRanking;
}
}
//CAREFUL Can Be Empty
return ContestRankings;
}
UFUNCTION(BlueprintCallable, Category="DTFlux|Model|Helpers")
static bool GetParticipant(const int Bib, FDTFluxParticipant& Participant)
{
UDTFluxSubsystem* Subsystem= GEngine->GetEngineSubsystem<UDTFluxSubsystem>();
UDTFluxDataStorage* DataStorage = Subsystem->GetDataStorage();
return DataStorage->GetParticipantByBib(Bib, Participant);
}
UFUNCTION(BlueprintCallable, Category="DTFlux|Model|Helpers")
static FString GetParticipantString(const int Bib, bool Truncate = true, int MaxSize = 20)
{
FString ParticipantStr = "";
FDTFluxParticipant Participant;
if(UDTFluxModelHelper::GetParticipant(Bib, Participant))
{
ParticipantStr = Participant.GetParticipantFormatedName(Truncate, MaxSize);
}
return ParticipantStr;
}
UFUNCTION(BlueprintCallable, Category="DTFlux|Model|Helpers")
static EDTFluxStageStatusType GetStatusType(const int ContestID, const int StageID, UDTFluxDataStorage* DataStorage);
UFUNCTION(BlueprintCallable, Category="DTFlux|Model|Helpers")
static int GetCurrentContest(UDTFluxDataStorage* DataStorage);
UFUNCTION(BlueprintCallable, Category="DTFlux|Model|Helpers")
static TArray<int> GetCurrentStage(UDTFluxDataStorage* DataStorage);
UFUNCTION(BlueprintCallable, Category="DTFlux|Model|Helpers")
static FString GetCurrentStageName(UDTFluxDataStorage* DataStorage);
};

View File

@ -0,0 +1,62 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "IWebSocket.h"
#include "UObject/Object.h"
#include "DTFluxWebsocketClient.generated.h"
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnConnectionConnected);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnConnectionClosed, const FString&, Reason);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnConnectionError, const FString&, Error);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnReceivedMessage, const FString&, Message);
UCLASS(Blueprintable, Category="DTFlux|Websocket")
class DTFLUXAPI_API UDTFluxWebSocketClient : public UObject
{
GENERATED_BODY()
public:
// UDTFluxWebSocketClient() = default;
void Initialize();
// virtual void BeginDestroy() override;
// virtual ~UDTFluxWebSocketClient() override;
UFUNCTION(BlueprintCallable, Category="DTFlux|Websocket")
bool Connect(const FString URL, const int Port );
UFUNCTION(BlueprintCallable, Category="DTFlux|Websocket")
void Disconnect();
UFUNCTION(BlueprintCallable, Category="DTFlux|Websocket")
bool SendMessage(const FString Message, const bool Broadcast = false);
UPROPERTY(BlueprintAssignable, Category="DTFlux|Websocket")
FOnConnectionConnected OnConnectionConnected;
UPROPERTY(BlueprintAssignable, Category="DTFlux|Websocket")
FOnConnectionClosed OnConnectionClosed;
UPROPERTY(BlueprintAssignable, Category="DTFlux|Websocket")
FOnConnectionError OnConnectionError;
UPROPERTY(BlueprintAssignable, Category="DTFlux|Websocket")
FOnReceivedMessage OnReceivedMessage;
bool Close() const;
bool IsConnected();
protected:
private:
void OnConnectionConnectedInternal() const;
void OnConnectionErrorInternal(const FString& Error) const;
void OnConnectionClosedInternal(const FString& Reason) const;
void OnReceivedMessageInternal(const FString& Message) const;
TSharedPtr<IWebSocket> Ws;
};

View File

@ -0,0 +1,131 @@
// // Fill out your copyright notice in the Description page of Project Settings.
//
// #pragma once
//
// #include "CoreMinimal.h"
// #include "UObject/Object.h"
// #include "IWebSocketServer.h"
// #include "DTFluxWebsocketServer.generated.h"
//
//
//
// DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnConnectionConnected, const FString&, Channel);
// DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnConnectionClosed, const FString&, Channel, const FString&, Reason);
// DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnConnectionError, const FString&, Channel, const FString&, Error);
// DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnReceivedMessage, const FString&, Channel, const FString&, Message);
// class INetworkingWebSocket;
//
// UCLASS(Blueprintable, Category="DTFlux|Websocket")
// class DTFLUXAPI_API UDTFluxWebsocketServer : public UObject
// {
// GENERATED_BODY()
//
// public:
//
// UDTFluxWebsocketServer();
//
//
//
// void Initialize(const FString& Channel)
// {
// ChannelName = Channel;
// };
//
// virtual ~UDTFluxWebsocketServer() override;
//
// UFUNCTION(BlueprintCallable, Category="DTFlux|Websocket")
// bool ConnectToChannel(const FString URL, const int Port );
//
// UFUNCTION(BlueprintCallable, Category="DTFlux|Websocket")
// void LeaveChannel();
//
// UFUNCTION(BlueprintCallable, Category="DTFlux|Websocket")
// bool SendMessageToAll(const FString Message);
//
// UPROPERTY(BlueprintAssignable, Category="DTFlux|Websocket")
// FOnConnectionConnected OnConnectionConnected;
//
// UPROPERTY(BlueprintAssignable, Category="DTFlux|Websocket")
// FOnConnectionClosed OnConnectionClosed;
//
// UPROPERTY(BlueprintAssignable, Category="DTFlux|Websocket")
// FOnConnectionError OnConnectionError;
//
// UPROPERTY(BlueprintAssignable, Category="DTFlux|Websocket")
// FOnReceivedMessage OnReceivedMessage;
//
// UPROPERTY(BlueprintReadOnly, Category="DTFlux|Websocket")
// FString ChannelName;
//
// void Close();
//
//
// protected:
//
//
//
// private:
//
// bool bIsConnected = false;
// void OnConnectionConnectedInternal(INetworkingWebSocket* ClientWebSocket);
// void OnConnectionErrorInternal();
// // void OnConnectionClosedInternal(const FString& Reason);
// void OnReceivedMessageInternal(void* Data, int32 Count);
//
// static TUniquePtr<IWebSocketServer> GetServer();
//
// TArray<INetworkingWebSocket*> Clients;
// // TSharedPtr<IWebSocket> Ws;
// TUniquePtr<IWebSocketServer> Ws;
// };
#pragma once
#include "IWebSocketServer.h"
#include "Subsystems/GameInstanceSubsystem.h"
#include "DTFluxWebsocketServer.generated.h"
UCLASS(Blueprintable, Category="DTFlux|Websocket")
class DTFLUXAPI_API UDTFluxServerWebSocket : public UObject
{
GENERATED_BODY()
public:
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnConnectionConnected);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnConnectionClosed, const FString&, Reason);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnConnectionError, const FString&, Error);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnReceivedMessage, const FString&, Message);
UDTFluxServerWebSocket();
virtual ~UDTFluxServerWebSocket() override;
void Init(const int& Port, const FString& Url = TEXT("ws://localhost"));
virtual void BeginDestroy() override;
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnJsonRecieved, const FString&, Payload);
UPROPERTY(BlueprintAssignable)
FOnJsonRecieved OnJsonRecieved;
void Close()
{
ServerWebSocket = nullptr;
};
protected:
void OnWebSocketClientConnected(INetworkingWebSocket* ClientWebSocket); // to the server.
virtual void ReceivedRawPacket(void* Data, int32 Count);
private:
TUniquePtr<class IWebSocketServer> ServerWebSocket;
TMap<FGuid,INetworkingWebSocket*> ConnectedClients;
/** Delegate for callbacks to GameThreadTick */
FTSTicker::FDelegateHandle TickHandle;
};