First Commit with code

This commit is contained in:
2024-06-08 09:41:08 +02:00
parent c512b76388
commit e5ed930002
15 changed files with 1356 additions and 0 deletions

View File

@ -0,0 +1,36 @@
// 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"
}
);
PrivateDependencyModuleNames.AddRange(
new string[]
{
"CoreUObject",
"Engine",
"Slate",
"SlateCore",
"HTTPServer",
"HTTP",
"DeveloperToolSettings",
"DeveloperSettings",
"Json",
"JsonUtilities",
"AvalancheCore",
"AvalancheMedia",
}
);
}
}

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,4 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "DTFluxModel/DTFluxModel.h"

View File

@ -0,0 +1,73 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "DTFluxProjectSettings/DTFluxProjectSettings.h"
FString UDTFluxProjectSettings::GetAPIPath(const TEnumAsByte<EDTFluxAPIRoute> RouteType, const FString& Filters) const
{
FString ApiAccessRoute;
UE_LOG(LogDTFluxAPI, Type::Log, TEXT("Filters in GetApiPath settings\nGot %s"), *Filters);
if (bAccessIsLocal)
{
// http://localhost/_246158/api/8O8JMI2739JS58R8KRJGJEZUSQXF807O
ApiAccessRoute += RaceResultPort != 80 ? FString::Printf(TEXT("%s:%i/_%s/api/"), *ProxyUrl, RaceResultPort, *APIToken) :
FString::Printf(TEXT("%s/_%s/api/"), *ProxyUrl, *APIToken);
}
else
{
// https://api.raceresult.com/246158/8O8JMI2739JS58R8KRJGJEZUSQXF807O
ApiAccessRoute += FString::Printf(TEXT("https://api.raceresult.com/%s/"), *APIToken);
}
switch(RouteType)
{
case (EDTFluxAPIRoute::Results):
return ApiAccessRoute + LiveStageResultsAccessToken + Filters;
break;
case (EDTFluxAPIRoute::FinalClassification):
return ApiAccessRoute + GeneralClassificationAccessToken + Filters;
break;
default:
return ApiAccessRoute + StartListAccessToken + Filters;
}
}
FString UDTFluxProjectSettings::GetAPIPathFiltered(const TEnumAsByte<EDTFluxAPIRoute> RouteType,
const FSearchFilters& Filters) const
{
const FString Filter = Filters.GetFilter();
UE_LOG(LogDTFluxAPI, Type::Log, TEXT("Filters in Project settings\nGot %s"), *Filter);
return GetAPIPath(RouteType, Filter );
}
FString UDTFluxProjectSettings::GetProxyPath(const TEnumAsByte<EDTFluxProxyRoute> RouteType,
const int& InContest, const int& InStage ) const
{
switch(RouteType)
{
case (EDTFluxProxyRoute::ProxyRankingContest):
return FString::Printf(TEXT("%s:%i%s/ranking/contest/%i/"), *ProxyAddress, ProxyPort, *ProxyRootPath, InContest);
break;
case (EDTFluxProxyRoute::ProxyRankingStage):
return FString::Printf(TEXT("%s:%i%s/ranking/contest/%i/stage?id=%i"),
*ProxyAddress, ProxyPort, *ProxyRootPath, InContest, InStage);
break;
case (EDTFluxProxyRoute::ProxyTeams):
return FString::Printf(TEXT("%s:%i%s/teams/"), *ProxyAddress, ProxyPort, *ProxyRootPath);
break;
default :
return FString::Printf(TEXT("%s:%i%s/race/datas/"), *ProxyAddress, ProxyPort, *ProxyRootPath);
break;
}
}
const UDTFluxProjectSettings* UDTFluxProjectSettings::GetDTFluxAPIProjectSettings()
{
return GetDefault<UDTFluxProjectSettings>();
}
UDTFluxProjectSettings::UDTFluxProjectSettings()
{
SectionName = "DTFlux Settings";
}

View File

@ -0,0 +1,331 @@
// 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 "HttpServerModule.h"
#include "HttpRouteHandle.h"
#include "DTFluxAPILog.h"
#include "IHttpRouter.h"
#include "HttpModule.h"
#include "JsonObjectConverter.h"
#include "Interfaces/IHttpResponse.h"
// TODO: Not implemented
bool UDTFluxSubsystem::OnRequest(const FHttpServerRequest& Request)
{
return true;
}
// TODO: Not implemented
void UDTFluxSubsystem::HandleRequest(const FString& Route,const FHttpServerRequest& Request, FHttpResultCallback OnComplete)
{
// creating payload string
FString ReqPayload;
if (Request.Body.Num() > 0)
{
const std::string RawBody((char*)Request.Body.GetData(), Request.Body.Num());
ReqPayload = UTF8_TO_TCHAR(RawBody.c_str());
}
TSharedPtr<FJsonObject> JsonPayload;
if(!FJsonSerializer::Deserialize(TJsonReaderFactory<>::Create(ReqPayload), JsonPayload))
{
UE_LOG(LogDTFluxAPI, Error, TEXT("unable to parse JSON Payload\n%s"), *ReqPayload);
}
//Checking path
UE_LOG(LogDTFluxAPI,Log, TEXT("Request Received for Route %s"), *Route);
// EventStart
if(Route == TEXT("/event/start"))
{
FDTFluxStartStagePayload StartStagePayload;
FJsonObjectConverter::JsonObjectToUStruct<FDTFluxStartStagePayload>(JsonPayload.ToSharedRef(), &StartStagePayload, 0, 0);
OnEventStartReceived.Broadcast(StartStagePayload);
}
else if(Route == TEXT("/team/create"))
{
// /team/create route
}
else if(Route == TEXT("/team/update"))
{
// /team/create route
} else if(Route == TEXT("/team/create"))
{
// /team/create route
}
// Default Route
else
{
}
//Preparing Response Header
TUniquePtr<FHttpServerResponse> Response = CreateHttpServerResponse();
// Adding Response Body
FDTFluxResponseBody RespBody;
RespBody.Success = TEXT("OK");
std::string RespBody_c;
RespBody_c = TCHAR_TO_UTF8(*RespBody.Deserialize());
Response->Body.Append((const uint8*)RespBody_c.c_str(), RespBody_c.length());
// Return the response
OnComplete(MoveTemp(Response));
}
void UDTFluxSubsystem::OnUpdateStartList(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful)
{
if (!bWasSuccessful || !Response.IsValid())
{
UE_LOG(LogDTFluxAPI, Error, TEXT("RaceResult Request failed"));
return;
}
// Tricks because Payload root is an array
const FString ModifiedData = FString::Printf(TEXT("{\"Participants\":%s}"), *Response->GetContentAsString());
TSharedPtr<FJsonObject> Payload;
if(!FJsonSerializer::Deserialize(TJsonReaderFactory<>::Create(ModifiedData), Payload))
{
UE_LOG(LogDTFluxAPI, Error, TEXT("unable to parse JSON Payload****"));
}else
{
FDTFluxStartListPayload StartList;
if (FJsonObjectConverter::JsonObjectToUStruct<FDTFluxStartListPayload>(Payload.ToSharedRef(), &StartList, 0, 0))
{
UE_LOG(LogDTFluxAPI, Log, TEXT("Success Reading"))
for(const auto& Participant : StartList.Participants)
{
// Creating a new Contest
if(!Contests.Contests.Contains(Participant.ContestName))
{
FDTFluxContest NewContest;
NewContest.ContestID = Participant.ContestID;
NewContest.ContestName = Participant.ContestName;
Contests.Contests.Add(Participant.ContestName, NewContest);
}
FDTFluxContest* CurrentContest = Contests.Contests.Find(Participant.ContestName);
FDTFluxTeam Team;
Team.Bib = Participant.Bib;
FDTFluxParticipant ContestParticipant;
ContestParticipant.FirstName = Participant.FirstName;
ContestParticipant.LastName = Participant.LastName;
ContestParticipant.Gender = Participant.Gender;
ContestParticipant.Club = Participant.Club;
ContestParticipant.Category = Participant.Category;
Team.Participants.Add(ContestParticipant);
// if we have LastName2 it is a two personne Team
if(Participant.LastName2 != "")
{
FDTFluxParticipant ContestParticipant2;
ContestParticipant2.FirstName = Participant.FirstName2;
ContestParticipant2.LastName = Participant.LastName2;
ContestParticipant2.Gender = Participant.Gender2;
ContestParticipant2.Club = Participant.Club2;
ContestParticipant2.Category = Participant.Category;
Team.Participants.Add(ContestParticipant);
Team.TeamName = Participant.TeamName;
UE_LOG(LogDTFluxAPI, Log, TEXT("Participant is a team : TeamName \"%s\" "), *Team.TeamName);
}
else
{
Team.TeamName = Participant.FirstName + TEXT(" ") + Participant.LastName.ToUpper();
}
// check if Participant already exists
if(!CurrentContest->TeamAlreadyExist(Team))
{
// Add it to the TeamList
CurrentContest->AddTeam(Team);
UE_LOG(LogDTFluxAPI, Log, TEXT("Adding Team \"%s\" "), *Team.TeamName);
}
}
UE_LOG(LogDTFluxAPI, Log, TEXT("Contest has now %i elements"), Contests.Contests.Num());
}
else
{
UE_LOG(LogDTFluxAPI, Error, TEXT("Error Deserializing Payload \n*%s*\n"), *ModifiedData);
}
}
for(const auto& Element : Contests.Contests)
{
UE_LOG(LogDTFluxAPI, Log, TEXT("Contest %s"), *Element.Key);
FDTFluxContest Contest = Element.Value;
UE_LOG(LogDTFluxAPI, Log, TEXT("Number of participants in this contest %i"), Contest.TeamParticipants.Num());
}
}
TUniquePtr<FHttpServerResponse> UDTFluxSubsystem::CreateHttpServerResponse() const
{
// Create a Response to be returned by the server
TUniquePtr<FHttpServerResponse> Response = MakeUnique<FHttpServerResponse>();
Response->Code = EHttpServerResponseCodes::Ok;
// Response Header
Response->Headers.Add(TEXT("Content-Type"), { TEXT("application/json;charset=utf-8") });
Response->Headers.Add(TEXT("Access-Control-Allow-Origin"), { TEXT("*") });
Response->Headers.Add(TEXT("Access-Control-Allow-Methods"), { TEXT("GET,POST,PUT,PATCH,DELETE,OPTIONS") });
Response->Headers.Add(TEXT("Access-Control-Allow-Headers"), { TEXT("Origin,X-Requested-With,Content-Type,Accept") });
Response->Headers.Add(TEXT("Access-Control-Max-Age"), { TEXT("600") });
Response->Headers.Add(TEXT("Access-Control-Allow-Credentials"), { TEXT("true") });
Response->Headers.Add(TEXT("Server"), { TEXT("UE 5.4.1 EMBBEDED") });
return Response;
}
void UDTFluxSubsystem::Initialize(FSubsystemCollectionBase& Collection)
{
Super::Initialize(Collection);
mSettings = UDTFluxProjectSettings::GetDTFluxAPIProjectSettings();
if(mSettings)
{
// Setting up request
HttpRequest = &FHttpModule::Get();
HttpRouter = FHttpServerModule::Get().GetHttpRouter(mSettings->InPort);
if(!HttpRouter)
{
UE_LOG(LogDTFluxAPI, Type::Error, TEXT("Invalid Http Router for port : %i"), mSettings->InPort)
}
// Setting Up Routes
for(const TArray<FString> Routes = mSettings->Endpoints; const FString& Route : Routes)
{
const FHttpRequestHandler OptionRequestHandler = FHttpRequestHandler::CreateLambda([this](const FHttpServerRequest &Request, const FHttpResultCallback& OnComplete)
{
OnComplete(CreateHttpServerResponse());
return true;
});
FHttpRouteHandle OptionRouteHandle = HttpRouter->BindRoute(Route, EHttpServerRequestVerbs::VERB_OPTIONS, OptionRequestHandler);
HttpMountedMap.Add(Route + TEXT("HTTPOption"), OptionRouteHandle);
const FHttpRequestHandler RequestHandler = FHttpRequestHandler::CreateLambda([this, Route](const FHttpServerRequest& Request, const FHttpResultCallback& OnComplete)
{
// Processing Request to Raw OnRequestReceived firing
FDTFluxHttpServerHeaders Headers;
for(const auto &Header: Request.Headers)
{
Headers.Headers.Add(Header.Key, FString::Join(Header.Value, TEXT(",")));
}
FDTFluxHttpServerParams Params;
Params.Params = Request.QueryParams;
FDTFluxHttpServerBody Payload;
Payload.ReqBody = Request.Body;
// Raw broadcasting Received event
OnRequestReceived.Broadcast(Headers, Params, Payload);
HandleRequest(Route, Request, OnComplete);
return true;
});
// Binding Routes
FHttpRouteHandle RouteHandle = HttpRouter->BindRoute(Route, EHttpServerRequestVerbs::VERB_POST, RequestHandler);
HttpMountedMap.Add(Route, RouteHandle);
}
}
}
void UDTFluxSubsystem::Deinitialize()
{
StopServer();
UE_LOG(LogDTFluxAPI, Log, TEXT("Route Num %i"), HttpMountedMap.Num());
for( auto const& Route: HttpMountedMap)
{
HttpRouter->UnbindRoute(Route.Value);
}
HttpMountedMap.Empty();
HttpRouter.Reset();
Super::Deinitialize();
}
TArray<FString> UDTFluxSubsystem::GetMountedRoutes() const
{
TArray<FString> Mounted = TArray<FString>();
for(const auto Route : HttpMountedMap)
{
Mounted.Add(Route.Key);
}
return Mounted;
}
void UDTFluxSubsystem::StartServer()
{
if(bIsListening){return ;}
FHttpServerModule::Get().StartAllListeners();
bIsListening = true;
OnServerListening.Broadcast();
}
TArray<FDTFluxTeam> UDTFluxSubsystem::GetParticipantsByContestId(const int ContestId)
{
if(Contests.Contests.Num() != 0)
{
FString ContestName = Contests.GetContestName(ContestId);
UE_LOG(LogDTFluxAPI, Log, TEXT("Getting Participants for Contest %s"), *ContestName);
return GetParticipantsByContestName(ContestName);
}
else
{
UE_LOG(LogDTFluxAPI, Error, TEXT("No Contest Yet !!!!"));
TArray<FDTFluxTeam> EmptyTeam;
return EmptyTeam;
}
}
TArray<FDTFluxTeam> UDTFluxSubsystem::GetParticipantsByContestName(const FString ContestName)
{
if(Contests.Contests.Num() != 0)
{
return Contests.Contests[ContestName].TeamParticipants;
}
else
{
UE_LOG(LogDTFluxAPI, Error, TEXT("No Contest Yet !!!!"));
TArray<FDTFluxTeam> EmptyTeam;
return EmptyTeam;
}
}
FString UDTFluxSubsystem::GetContestName(const int ContestId)
{
if(Contests.GetContestName(ContestId) != "")
{
return Contests.GetContestName(ContestId);
}
return TEXT("");
}
void UDTFluxSubsystem::UpdateStartList()
{
const TSharedRef<IHttpRequest> Req = HttpRequest->CreateRequest();
Req->SetVerb("GET");
Req->SetURL(mSettings->GetAPIPath(EDTFluxAPIRoute::Starters));
Req->SetHeader(TEXT("Content-Type"), TEXT("application/json"));
Req->OnProcessRequestComplete().BindUObject(this, &UDTFluxSubsystem::OnUpdateStartList);
Req->ProcessRequest();
}
void UDTFluxSubsystem::UpdateClassification(const int& ContestId, const int& StageId)
{
}
TArray<FDTFluxParticipant> UDTFluxSubsystem::GetClassification(const int& ContestId, const int& StageId)
{
return TArray<FDTFluxParticipant>();
}
void UDTFluxSubsystem::StopServer()
{
FHttpServerModule::Get().StopAllListeners();
bIsListening = false;
OnServerStopped.Broadcast();
}

View File

@ -0,0 +1,38 @@
// Copyright 2023 Dexter.Wan. All Rights Reserved.
// EMail: 45141961@qq.com
#include "DTFluxUtils/DTFluxHttpServerStruct.h"
void UDTFluxHttpServerBPFn::BreakParams(const FDTFluxHttpServerParams& HttpServerParams, TMap<FString, FString>& Params)
{
Params = HttpServerParams.Params;
}
void UDTFluxHttpServerBPFn::FindParam(const FDTFluxHttpServerParams& HttpServerParams, const FString& Key, FString& Param)
{
Param.Empty();
if ( const FString * pParam = HttpServerParams.Params.Find(Key) )
{
Param = *pParam;
}
}
void UDTFluxHttpServerBPFn::BreakHeaders(const FDTFluxHttpServerHeaders& HttpServerHeaders, TMap<FString, FString>& Headers)
{
Headers = HttpServerHeaders.Headers;
}
void UDTFluxHttpServerBPFn::BreakBody(const FDTFluxHttpServerBody& HttpBody, TArray<uint8> RawBody)
{
RawBody = HttpBody.ReqBody;
}
void UDTFluxHttpServerBPFn::FindHeader(const FDTFluxHttpServerHeaders& HttpServerHeaders, const FString& Key, FString& Header)
{
Header.Empty();
if ( const FString * pHeader = HttpServerHeaders.Headers.Find(Key) )
{
Header = *pHeader;
}
}

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,423 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "UObject/Object.h"
#include "DTFluxModel.generated.h"
// Forward declarations
struct FDTFluxTeam;
USTRUCT(BlueprintType, Category="DTFlux|Model")
struct DTFLUXAPI_API FDTFLuxStartStageData
{
GENERATED_BODY()
UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly)
FString Type;
UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly)
FString Contest;
UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly)
FString Stage;
// Maybe this one can be made DATETIME
UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly)
FString RealStartTime;
};
USTRUCT(BlueprintType, Category="DTFlux|Model")
struct DTFLUXAPI_API FDTFluxStartStagePayload
{
GENERATED_BODY()
UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly)
FString Description;
UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly)
FString Trigger;
UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly)
FString Type;
UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly)
TArray<FDTFLuxStartStageData> Datas;
};
USTRUCT(BlueprintType, Category="DTFlux|Model")
struct DTFLUXAPI_API FDTFluxStartListItemPayload
{
GENERATED_BODY()
// "Report": "Start List",
// "ContestID": 0,
// "ContestName": "",
// "Bib": 1039,
// "Firstname": "Mystère",
// "Lastname": "Mystère",
// "Gender": "",
// "Club": "",
// "Firstname2": "",
// "Lastname2": "",
// "Gender2": "",
// "Club2": "",
// "TeamName": "",
// "Category": "",
// "Elite": false
UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly)
int ContestID;
UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly)
FString ContestName;
UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly)
int Bib;
UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly)
FString FirstName;
UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly)
FString LastName;
UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly)
FString Gender;
UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly)
FString Club;
UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly)
FString FirstName2;
UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly)
FString LastName2;
UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly)
FString Gender2;
UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly)
FString Club2;
UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly)
FString TeamName;
UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly)
FString Category;
UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly)
bool Elite;
};
USTRUCT(BlueprintType, Category="DTFlux|Model")
struct DTFLUXAPI_API FDTFluxStartListPayload
{
GENERATED_BODY()
UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly)
TArray<FDTFluxStartListItemPayload> Participants;
};
USTRUCT(BlueprintType, Category="DTFlux|Model")
struct DTFLUXAPI_API FDTFluxParticipant
{
GENERATED_BODY()
public:
UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly)
FString FirstName;
UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly)
FString LastName;
UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly)
FString Club;
UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly)
FString Gender;
UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly)
FString Category;
};
USTRUCT(BlueprintType, Category="DTFlux|Model")
struct DTFLUXAPI_API FDTFluxTeam
{
GENERATED_BODY()
public:
UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly)
TArray<FDTFluxParticipant> Participants;
UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly)
FString TeamName;
UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly)
int Bib;
};
USTRUCT(BlueprintType, Category="DTFlux|Model")
struct FDTFluxContest
{
GENERATED_BODY()
UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly)
int ContestID;
UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly)
FString ContestName;
UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly)
TArray<FDTFluxTeam> TeamParticipants;
bool AddTeam(FDTFluxTeam Team)
{
TeamParticipants.Add(Team);
return true;
}
bool TeamAlreadyExist(FDTFluxTeam Team)
{
for(const auto& MyTeam : TeamParticipants)
{
// Compare Bib
if(MyTeam.Bib == Team.Bib )
{
return true;
}
}
return false;
}
};
USTRUCT(BlueprintType, Category="DTFlux|Model")
struct DTFLUXAPI_API FDTFluxContestList
{
GENERATED_BODY()
UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly)
TMap<FString, FDTFluxContest>Contests;
int GetContestId(const FString& ContestName) const
{
const FDTFluxContest* Contest = Contests.Find(ContestName);
if(Contest)
{
return Contest->ContestID;
}
return -1;
}
FString GetContestName(const int& ContestId) const
{
for (const auto& Contest : Contests)
{
if (Contest.Value.ContestID == ContestId)
{
return Contest.Key;
}
}
return FString();
}
};
UCLASS(Blueprintable, Category="DTFlux|Model")
class DTFLUXAPI_API UDTFluxContestBPFn : public UBlueprintFunctionLibrary
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintPure, Category = "DT Http Server|Params")
static int GetContestId(const FDTFluxContestList& ContestList, const FString& ContestName)
{
return ContestList.GetContestId(ContestName);
}
UFUNCTION(BlueprintPure, Category = "DT Http Server|Params")
static FString GetContestName(const FDTFluxContestList& ContestList, const int& ContestId)
{
return ContestList.GetContestName(ContestId);
}
};
// TeamListItem Response from proxy Containing Team definition
USTRUCT(BlueprintType, Category="DTFlux|Model")
struct FResponseTeamListItem
{
GENERATED_BODY()
UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly)
FString Contest;
UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly)
int Bib;
UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly)
FString FirstName;
UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly)
FString LastName;
UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly)
FString Gender;
UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly)
bool Elite;
UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly)
FString Club;
UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly)
FString Team;
UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly)
FString Category;
UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly)
FString Status;
};
// TeamList Response from proxy Containing List of Team
USTRUCT(BlueprintType, Category="DTFlux|Model")
struct FProxyResponseTeamList
{
GENERATED_BODY()
UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly)
TArray<FResponseTeamListItem> Datas;
};
USTRUCT(BlueprintType, Category="DTFlux|Model")
struct FProxyResponseStageListItem
{
GENERATED_BODY()
UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly)
int Id;
UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly)
FString Name;
UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly)
FString StartTime;
UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly)
FString EndTime;
};
USTRUCT(BlueprintType, Category="DTFlux|Model")
struct FProxyResponseSplitsListItem
{
GENERATED_BODY()
UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly)
int Id;
UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly)
FString Name;
};
USTRUCT(BlueprintType, Category="DTFlux|Model")
struct FProxyResponseRaceListItem
{
GENERATED_BODY()
UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly)
int Id;
UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly)
FString Name;
UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly)
FString Date;
UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly)
TArray<FProxyResponseStageListItem> Stages;
UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly)
TArray<FProxyResponseSplitsListItem> Splits;
};
USTRUCT(BlueprintType, Category="DTFlux|Model")
struct FProxyResponseContestRankingListItem
{
GENERATED_BODY()
UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly)
int Bib;
UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly)
int Rank;
UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly)
FString Time;
UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly)
FString Gap;
};
USTRUCT(BlueprintType, Category="DTFlux|Model")
struct FProxyResponseContestRankingList
{
GENERATED_BODY()
UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly)
int ContestId;
UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly)
TArray<FProxyResponseContestRankingListItem> Datas;
};
USTRUCT(BlueprintType, Category="DTFlux|Model")
struct FProxyResponseStageRankingListItem
{
GENERATED_BODY()
UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly)
int Bib;
UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly)
int Rank;
UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly)
FString Time;
UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly)
FString Gap;
UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly)
FString TimeSwim;
UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly)
FString TimeTransition;
UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly)
FString TimeRun;
UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly)
FString TimeStart;
};
USTRUCT(BlueprintType, Category="DTFlux|Model")
struct FProxyResponseStageRankingList
{
GENERATED_BODY()
UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly)
int ContestId;
UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly)
int StageId;
UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly)
TArray<FProxyResponseStageRankingListItem> Datas;
};

View File

@ -0,0 +1,154 @@
// 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"
UENUM()
enum EDTFluxAPIRoute: uint8
{
Root UMETA(DisplayName="API Root"),
Results UMETA(DisplayName="Results Route"),
Starters UMETA(DisplayName="Starters List Route"),
FinalClassification UMETA(DisplayName="Final Classification Route"),
};
UENUM()
enum EDTFluxProxyRoute : uint8
{
ProxyRaceData UMETA(DisplayName="Proxy Race Data"),
ProxyRankingContest UMETA(DisplayName="Proxy Ranking Contest Data"),
ProxyRankingStage UMETA(DisplayName="Proxy Ranking Stage Data"),
ProxyTeams UMETA(DisplayName="Proxy Teams Data"),
};
USTRUCT(BlueprintType)
struct FSearchFilters
{
GENERATED_BODY()
UPROPERTY(Blueprintable, Category="DTFlux|Config", EditAnywhere)
int ContestId = 0;
UPROPERTY(Blueprintable, Category="DTFlux|Config", EditAnywhere)
int StageId = 0;
UPROPERTY(Blueprintable, Category="DTFlux|Config", EditAnywhere)
int GenderId = 0;
FString GetFilter() const
{
if(ContestId == 0 && StageId == 0 && GenderId == 0)
{
UE_LOG(LogDTFluxAPI, Type::Log, TEXT("Gender Test -> %i"), GenderId);
return FString();
}
TArray<FString> Filters;
Filters.Add(FString("?"));
if (ContestId > 0)
{
Filters.Add(FString::Printf(TEXT("Contest=%i"), ContestId) );
}
if (StageId > 0)
{
Filters.Add(FString::Printf(TEXT("SelectorResult=%i"), StageId) );
}
if (GenderId > 0)
{
switch(GenderId)
{
case (2):
// default is male
UE_LOG(LogDTFluxAPI, Type::Log, TEXT("Gender Female -> %i"), GenderId);
Filters.Add(FString(TEXT("filter=[Gender]=\"f\"") ) );
break;
default:
// default is male
UE_LOG(LogDTFluxAPI, Type::Log, TEXT("Gender male -> %i"), GenderId);
Filters.Add(FString(TEXT("filter=[Gender]=\"m\"") ) );
break;
}
}
for(const auto& Element : Filters)
{
UE_LOG(LogDTFluxAPI, Type::Log, TEXT("Element : %s"), *Element);
}
FString FinalFilters = FString::Join(Filters, TEXT("&"));
FinalFilters.RemoveAt(1);
return FinalFilters;
};
};
/**
*
*/
UCLASS(Blueprintable, Config=Engine, DefaultConfig, meta=(DisplayName="DTFlux Project Settings"))
class DTFLUXAPI_API UDTFluxProjectSettings : public UDeveloperSettings
{
GENERATED_BODY()
public:
UPROPERTY(Category="DTFlux|Server Config", Config, EditAnywhere, BlueprintReadOnly)
int InPort = 8080;
UPROPERTY(Category="DTFlux|Config|Race Result API", Config, EditAnywhere, BlueprintReadOnly)
int RaceResultPort = 80;
UPROPERTY(Category="DTFlux|Server Config", Config, EditAnywhere, BlueprintReadOnly)
TArray<FString> Endpoints;
UPROPERTY(Category="DTFlux|Config|Race Result API", Config, EditAnywhere, BlueprintReadOnly)
FString ProxyUrl = "http://localhost";
UPROPERTY(Category="DTFlux|Config|Race Result API", Config, EditAnywhere, BlueprintReadOnly)
FString APIToken;
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 ;
UPROPERTY(Category="DTFlux|Config|Chrono Proxy", Config, EditAnywhere, BlueprintReadOnly)
FString ProxyAddress = "http://localhost";
UPROPERTY(Category="DTFlux|Config|Chrono Proxy", Config, EditAnywhere, BlueprintReadOnly)
FString ProxyRootPath = "/endpoints";
UPROPERTY(Category="DTFlux|Chrono Proxy", Config, EditAnywhere, BlueprintReadOnly)
int ProxyPort = 8000;
UFUNCTION(BlueprintCallable, Category="DTFlux|Config")
FString GetAPIPath(const TEnumAsByte<EDTFluxAPIRoute> RouteType, const FString& Filters = TEXT("") ) const;
UFUNCTION(BlueprintCallable, Category="DTFlux|Config")
FString GetAPIPathFiltered(const TEnumAsByte<EDTFluxAPIRoute> RouteType, const FSearchFilters& Filters ) const;
UFUNCTION(BlueprintCallable, Category="DTFlux|Config")
FString GetProxyPath(const TEnumAsByte<EDTFluxProxyRoute> RouteType, const int& InContest = -1, const int& InStage = -1) const;
UFUNCTION(BlueprintCallable, Category="DTFlux|Config")
static const UDTFluxProjectSettings* GetDTFluxAPIProjectSettings();
protected:
private:
UDTFluxProjectSettings();
};

View File

@ -0,0 +1,153 @@
// 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 "HttpServerRequest.h"
#include "HttpResultCallback.h"
#include "HttpRouteHandle.h"
#include <string>
#include "DTFluxUtils/DTFluxHttpServerStruct.h"
#include "DTFluxAPILog.h"
#include "DTFluxModel/DTFluxModel.h"
#include "DTFluxSubsystem.generated.h"
class UDTFluxProjectSettings;
class IHttpRouter;
class IHttpRequest;
class IHttpResponse;
class FHttpModule;
typedef TSharedPtr<IHttpRequest, ESPMode::ThreadSafe> FHttpRequestPtr;
typedef TSharedPtr<IHttpResponse, ESPMode::ThreadSafe> FHttpResponsePtr;
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")
};
USTRUCT(BlueprintType, Category="DTFlux|Server")
struct FDTFluxResponseBody
{
GENERATED_BODY()
UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly)
FString Error;
UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly)
FString Success;
FString Deserialize()
{
FString JSONObject;
JSONObject += TEXT("{");
JSONObject += TEXT("\"error\":\"");
JSONObject += Error.IsEmpty() ? TEXT("") : Error;
JSONObject += TEXT("\",\"success\":\"");
JSONObject += Success.IsEmpty() ? TEXT("") : Success;
JSONObject += TEXT("\"}");
UE_LOG(LogDTFluxAPI, Log, TEXT("JSONObject : %s"), *JSONObject);
return JSONObject;
}
};
DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FDTFluxOnRequestReceived, FDTFluxHttpServerHeaders, HttpServerHeaders,
FDTFluxHttpServerParams, HttpServerParams, FDTFluxHttpServerBody, HttpRequestBody);
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FDTFluxOnServerListening);
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FDTFluxOnServerStopped);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FDTFluxOnEventStartReceived, FDTFluxStartStagePayload, Payload);
/**
* DTFlux API Subsystem
*
* This Subsystem Mount HTTP routes to be listened and an HTTP poller to retrieve basic information.
*/
UCLASS()
class DTFLUXAPI_API UDTFluxSubsystem : public UEngineSubsystem
{
GENERATED_BODY()
private:
bool bIsListening = false;
const UDTFluxProjectSettings* mSettings;
bool OnRequest(const FHttpServerRequest& Request);
void HandleRequest(const FString& Route, const FHttpServerRequest& Request, FHttpResultCallback OnComplete);
// // StartList Payload;
// FDTFluxStartListPayload StartList;
// Contest storage
FDTFluxContestList Contests;
void OnUpdateStartList(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful);
protected:
TMap<FString, FHttpRouteHandle> HttpMountedMap;
TSharedPtr<IHttpRouter> HttpRouter;
// Stop The Server
void StopServer();
// Create the server response
TUniquePtr<FHttpServerResponse> CreateHttpServerResponse() const;
FHttpModule* HttpRequest;
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;
UPROPERTY(BlueprintAssignable, Category="DTFlux|Subsystem|Events")
FDTFluxOnRequestReceived OnRequestReceived;
UPROPERTY(BlueprintAssignable, Category="DTFlux|Subsystem|Events")
FDTFluxOnServerListening OnServerListening;
UPROPERTY(BlueprintAssignable, Category="DTFlux|Subsystem|Events")
FDTFluxOnServerStopped OnServerStopped;
UPROPERTY(BlueprintAssignable, Category="DTFlux|Subsystem|Events")
FDTFluxOnEventStartReceived OnEventStartReceived;
UFUNCTION(BlueprintCallable, Category="DTFlux|Subsystem")
TArray<FString> GetMountedRoutes() const;
UFUNCTION(BlueprintCallable, Category="DTFlux|Subsystem")
void StartServer();
UFUNCTION(BlueprintCallable, Category="DTFlux|Subsystem|Events")
TArray<FDTFluxTeam> GetParticipantsByContestId(const int ContestId );
UFUNCTION(BlueprintCallable, Category="DTFlux|Subsystem|Events")
TArray<FDTFluxTeam> GetParticipantsByContestName(const FString ContestName);
UFUNCTION(BlueprintCallable, Category="DTFlux|Subsystem|Events")
FString GetContestName(const int ContestId);
UFUNCTION(BlueprintCallable, Category="DTFlux|Subsystem|Race Result Call")
void UpdateStartList();
UFUNCTION(BlueprintCallable, Category="DTFlux|Subsystem|Race Result Call")
void UpdateClassification(const int& ContestId, const int& StageId = -1);
UFUNCTION(BlueprintCallable, Category="DTFlux|Subsystem|Race Result Call")
TArray<FDTFluxParticipant> GetClassification(const int& ContestId, const int& StageId = -1);
};

View File

@ -0,0 +1,68 @@
// Copyright 2023 Dexter.Wan. All Rights Reserved.
// EMail: 45141961@qq.com
#pragma once
#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "DTFluxHttpServerStruct.generated.h"
UENUM(BlueprintType)
enum EDTFluxHttpServerVerbs : uint8
{
HTTP_GET,
HTTP_POST,
HTTP_PUT,
HTTP_PATCH,
HTTP_DELETE,
};
// USTRUCT(BlueprintType, meta=(DisplayName="DT Http Server Headers", HasNativeBreak = "DTHttpServer.DTHttpServerBPLib.BreakHeaders"))
USTRUCT(BlueprintType, Category="DTFlux|Server", meta=(HasNativeBreak = "DTFluxAPI.DTFluxHttpServerBPFn.BreakHeaders"))
struct FDTFluxHttpServerHeaders
{
GENERATED_BODY()
TMap<FString, FString> Headers;
};
USTRUCT(BlueprintType, Category="DTFlux|Server", meta=(HasNativeBreak = "DTFluxAPI.DTFluxHttpServerBPFn.BreakBody"))
struct FDTFluxHttpServerBody
{
GENERATED_BODY()
TArray<uint8> ReqBody;
};
// USTRUCT(BlueprintType, meta=(DisplayName="DTFlux Server Params", HasNativeBreak = "DTHttpServer.DTHttpServerBPLib.BreakParams"))
USTRUCT(BlueprintType, Category="DTFlux|Server", meta=(HasNativeBreak = "DTFluxAPI.DTFluxHttpServerBPFn.BreakParams"))
struct FDTFluxHttpServerParams
{
GENERATED_BODY()
TMap<FString, FString> Params;
};
UCLASS(NotBlueprintable, NotBlueprintType)
class DTFLUXAPI_API UDTFluxHttpServerBPFn : public UBlueprintFunctionLibrary
{
GENERATED_BODY()
public:
// Break DTFlux Http Server Params
UFUNCTION(BlueprintPure, meta = (DisplayName="Break Http Server Params"), Category = "DT Http Server|Params")
static void BreakParams(const FDTFluxHttpServerParams& HttpServerParams, TMap<FString, FString>& Params);
// Find DTFlux Http Server Params
UFUNCTION(BlueprintPure, meta = (DisplayName="Find Http Server Params"), Category = "DT Http Server|Params")
static void FindParam(const FDTFluxHttpServerParams& HttpServerParams, const FString & Key, FString & Param );
// Break DTFlux Http Server Headers
UFUNCTION(BlueprintPure, meta = (DisplayName="Break Http Server Headers"), Category = "DT Http Server|Headers")
static void BreakHeaders(const FDTFluxHttpServerHeaders& HttpServerHeaders, TMap<FString, FString>& Headers);
// Break DTFlux Request Body
UFUNCTION(BlueprintPure, meta = (DisplayName="Break Http Server Headers"), Category = "DT Http Server|Headers")
static void BreakBody(const FDTFluxHttpServerBody& HttpBody, TArray<uint8> RawBody);
// Find DTFlux Http Server Headers
UFUNCTION(BlueprintPure, meta = (DisplayName="Find Http Server Headers"), Category = "DT Http Server|Headers")
static void FindHeader(const FDTFluxHttpServerHeaders& HttpServerHeaders, const FString & Key, FString & Header );
};