// Fill out your copyright notice in the Description page of Project Settings. #include "cppGI.h" #include "Kismet/GameplayStatics.h" #include "Misc/paths.h" #include "Engine/levelstreamingdynamic.h" #include "cppFuncLibrary.h" #include "Misc/FileHelper.h" #include "Async/Async.h" /*for working tarray::Contains*/ static bool operator==(const Fcppcoords& c1, const Fcppcoords& c2) { return c1.flat == c2.flat && c1.floor == c2.floor && c1.house == c2.house && c1.section == c2.section && c1.type == c2.type && c1.zone == c2.zone; } /*for working tarray::Contains*/ static bool operator==(const FcppLevelStruct& c1, const FcppLevelStruct& c2) { return c1.anotherLvl == c2.anotherLvl && c1.coords == c2.coords && c1.dontUnload == c2.dontUnload && c1.path == c2.path && c1.uniqName == c2.uniqName && c1.Z == c2.Z; } TArray UcppGI::cppLvlManage(UObject* WorldContextObject, Fcppcoords currentCoords, uint8 currentState) { TArray outStruct; bool loadthis; auto is3DTour = currentState == 11; /*for 3Dtour only*/ if (is3DTour && currentCoords.house < 1) { cppLoadList.AddUnique(Fcppcoords(-1,0,currentCoords.zone,-2,-2,1)); } cppLoadList.AddUnique(currentCoords); for (const auto& lvl : cppLevels) { loadthis = 0; if (cppBlackList.Contains(lvl)); else if (cppWhiteList.Contains(lvl)) loadthis = true; else { bool ceq[7]; for (const auto& crd : cppLoadList) // here is filter code { cppCoordsEq(crd, lvl.coords, true, false, ceq[0], ceq[1], ceq[2], ceq[3], ceq[4], ceq[5], ceq[6]); if ( ( ( (ceq[0] || !is3DTour || lvl.coords.flat == -1) && ceq[5] ) || (is3DTour ? lvl.anotherLvl >= 1 && lvl.anotherLvl == crd.flat : lvl.anotherLvl >= 1 && lvl.coords.floor < crd.floor) ) && ceq[3] && ceq[4] && ceq[2] ) loadthis = true; //load all on floor and under 2-floor flat, and full 2-floor flat in 3d tour //if (ceq[6]) loadthis = true; //classic load with parallax } } if (loadthis) { if (cppCreatedList.Contains(lvl.uniqName)) { /*if already created level*/ ULevelStreaming* outlvl_ = nullptr; if ((outlvl_ = UGameplayStatics::GetStreamingLevel(WorldContextObject, lvl.uniqName)) != nullptr) { outlvl_->SetShouldBeLoaded(true); outlvl_->SetShouldBeVisible(true); cpploadedList.Add(lvl.uniqName); outStruct.Add(outlvl_); } } else { /*load new level instance*/ bool b; if (ULevelStreamingDynamic* newlvl = ULevelStreamingDynamic::LoadLevelInstance(WorldContextObject, lvl.path, FVector(0, 0, lvl.Z), FRotator(0), b, lvl.uniqName.ToString())) { newlvl->SetPriority(lvl.coords.flat == -1 ? 1 : 0); newlvl->bShouldBlockOnLoad = lvl.dontUnload; cppCreatedList.Add(lvl.uniqName); cpploadedList.Add(lvl.uniqName); outStruct.Add(newlvl); } } } else if (cppCreatedList.Contains(lvl.uniqName)) { ULevelStreaming* outlvl_ = nullptr; if ((outlvl_ = UGameplayStatics::GetStreamingLevel(WorldContextObject, lvl.uniqName)) != nullptr) { //if (lvl.path.EndsWith("refl")) outlvl_->bShouldBlockOnUnload = true; outlvl_->SetShouldBeLoaded(lvl.dontUnload); outlvl_->SetShouldBeVisible(false); cpploadedList.Remove(lvl.uniqName); outStruct.Add(outlvl_); } } } cppWhiteList.Empty(); cppBlackList.Empty(); cppLoadList.Empty(); return outStruct; } void UcppGI::cppCoordsEq(Fcppcoords coords, Fcppcoords coords1, bool relevantMinus2, bool fastEq, bool& flat_, bool& type_, bool& zone_, bool& house_, bool& section_, bool& floor_, bool& fullEq) { bool* outLvl[6] = { &flat_, &type_, &zone_, &house_, §ion_, &floor_ }; int32 c1[6] = { coords.flat,coords.type, coords.zone, coords.house, coords.section, coords.floor }; int32 c2[6] = { coords1.flat,coords1.type, coords1.zone, coords1.house, coords1.section, coords1.floor }; int32 mins[6] = { 0,0,1,1,1,0 }; if (fastEq) { for (int8 i = 0; i < 6; i++) { *outLvl[i] = (c1[i] == c2[i]); } } else { for (int8 i = 0; i < 6; i++) { *outLvl[i] = ((c1[i] < mins[i]) && (c2[i] < mins[i])) || (c1[i] == c2[i]) || ((c1[i] == -2) && relevantMinus2) || ((c2[i] == -2) && relevantMinus2); } } fullEq = true; for (const auto var : outLvl) { if (!*var)fullEq = false; } } TArray UcppGI::getFlatArr_(Fcppcoords coords, int& len) { auto output = TArray(); for (auto& flat : flatArray) { auto flatcoords = Fcppcoords(); flatcoords.flat = coords.flat < 0 ? -2 : flat.FlatId; //ignore flat flatcoords.floor = coords.floor < 0 ? -2 : flat.Floor; flatcoords.house = coords.house < 1 ? -2 : flat.House; flatcoords.section = coords.section < 1 ? -2 : flat.Section; flatcoords.type = coords.type < 0 ? -2 : flat.flatType; flatcoords.zone = coords.zone < 1 ? -2 : flat.Zone; bool dummy[6]; bool eq; cppCoordsEq(coords, flatcoords, true, false, dummy[0], dummy[1], dummy[2], dummy[3], dummy[4], dummy[5], eq); if (eq) output.Add(flat); } len = output.Num(); return output; } FcppflatStruct UcppGI::findFlatByCoords_(Fcppcoords coords, bool& success) { success = false; if (coords.flat < 0) return FcppflatStruct(); int len; auto tt = getFlatArr_(coords, len); if (len > 1) UE_LOG(LogCore, Warning, TEXT("UcppGI::findFlatByCoords: found more than one flat: GP%d-S%d-F%d-A%d"), coords.house, coords.section, coords.floor, coords.flat); if (len == 0) return FcppflatStruct(); success = true; return tt[0]; } FcppflatStruct UcppGI::findFlatByN_(Fcppcoords filter, int num, bool& success) { success = false; filter.flat = -2; auto output = FcppflatStruct(); auto arr = getFlatArr_(filter, intDummy); for (auto& flat : arr) { if (flat.FlatN == num) { if (!success) output = flat; else UE_LOG(LogCore, Warning, TEXT("UcppGI::findFlatByN_: found more than one flat")); success = true; } } return output; } void UcppGI::countFreeApartments(Fcppcoords coords, int& free, int& all, float& minPrice) { int len; free = 0; minPrice = 0; bool temp = false; for (auto& flat : getFlatArr_(coords, len)) { if (flat.available) { if (!temp&& flat.Price>100) { minPrice = flat.Price; temp = true; } free++; if (flat.Price < minPrice&& flat.Price>100) minPrice = flat.Price; } } minPrice /= 1000000; all = len; return; } void UcppGI::cppFlatTocppCoords(FcppflatStruct flat, Fcppcoords& coords) { coords.flat = flat.FlatId; coords.floor = flat.Floor; coords.house = flat.House; coords.section = flat.Section; coords.type = flat.flatType; coords.zone = flat.Zone; } void UcppGI::cppCoordsToCppFlat(UObject* WorldContextObject, Fcppcoords coords, FcppflatStruct& flat, bool onlyCoords) { if (onlyCoords) { flat.FlatId = coords.flat; flat.Floor = coords.floor; flat.House = coords.house; flat.Section = coords.section; flat.flatType = coords.type; flat.Zone = coords.zone; return; } bool dum; flat = StaticCastPtr(UGameplayStatics::GetGameInstance(WorldContextObject))->findFlatByCoords_(coords, dum); } void UcppGI::cpp_parseXmlAsync(UObject* WorldContextObject, const TMap& houseIds, FLatentActionInfo LatentInfo) { UWorld* World = WorldContextObject ? WorldContextObject->GetWorld() : nullptr; if (!World) return; struct FParseXmlLatent : public FPendingLatentAction { FName ExecutionFunction; int32 OutputLink; FWeakObjectPtr CallbackTarget; bool bDone = false; FParseXmlLatent(const FLatentActionInfo& Info) : ExecutionFunction(Info.ExecutionFunction) , OutputLink(Info.Linkage) , CallbackTarget(Info.CallbackTarget) {} virtual void UpdateOperation(FLatentResponse& Response) override { Response.FinishAndTriggerIf(bDone, ExecutionFunction, OutputLink, CallbackTarget); } }; FParseXmlLatent* Action = new FParseXmlLatent(LatentInfo); World->GetLatentActionManager().AddNewAction(LatentInfo.CallbackTarget, LatentInfo.UUID, Action); Async(EAsyncExecution::ThreadPool, [this, houseIds, Action]() { EEasyXMLParserFound fou; EEasyXMLParserErrorCode er; FString er1; auto xfile = UEasyXMLParseManager::LoadFromFile(FPaths::ProjectSavedDir() + FString("flatdata.xml"), true, er, er1); if (!xfile) { AsyncTask(ENamedThreads::GameThread, [Action]() { Action->bDone = true; }); return; } auto offers = xfile->ReadElements(FString("realty-feed.offer"), fou); if (fou != EEasyXMLParserFound::Found) { AsyncTask(ENamedThreads::GameThread, [Action]() { Action->bDone = true; }); return; } TMap flatKeyToOfferIdx; flatKeyToOfferIdx.Reserve(offers.Num()); for (int32 i = 0; i < offers.Num(); ++i) { auto& offer = offers[i]; const int32 houseId = offer->ReadInt(FString("yandex-house-id"), 0); if (const FIntPoint* housePoint = houseIds.Find(houseId)) { const int32 apartment = offer->ReadInt(FString("location.apartment"), 0); flatKeyToOfferIdx.Add({ housePoint->X, apartment }, i); } } struct FFlatUpdate { int32 Index; int32 Price; float Square; bool Available; FIntPoint Deadline; }; TArray updates; updates.Reserve(flatArray.Num()); for (int32 idx = 0; idx < flatArray.Num(); ++idx) { const auto& flat = flatArray[idx]; const int32* xmlFlatIdPtr = flatKeyToOfferIdx.Find({ flat.Zone, flat.FlatN }); if (!xmlFlatIdPtr) continue; const int32 xmlFlatId = *xmlFlatIdPtr; auto& xf = offers[xmlFlatId]; FFlatUpdate u; u.Index = idx; u.Price = xf->ReadInt(FString("price.value"), 0); u.Square = xf->ReadFloat(FString("area.value"), 0); u.Available = true; u.Deadline = { xf->ReadInt(FString("ready-quarter"), 0), xf->ReadInt(FString("built-year"), 0) }; updates.Add(MoveTemp(u)); } AsyncTask(ENamedThreads::GameThread, [this, Action, updates = MoveTemp(updates)]() mutable { for (const auto& u : updates) { if (!flatArray.IsValidIndex(u.Index)) continue; auto& flat = flatArray[u.Index]; flat.Price = u.Price; if (u.Square) flat.Square = u.Square; flat.available = u.Available; flat.deadline = u.Deadline; flat.price_meter = (flat.Price && flat.Square) ? flat.Price / flat.Square : 0; } Action->bDone = true; }); }); } void UcppGI::fixXmlAsync(UObject* WorldContextObject, FString xmlPath, FLatentActionInfo LatentInfo) { UWorld* World = WorldContextObject ? WorldContextObject->GetWorld() : nullptr; if (!World) return; struct FFixXmlLatent : public FPendingLatentAction { FName ExecutionFunction; int32 OutputLink; FWeakObjectPtr CallbackTarget; bool bDone = false; FFixXmlLatent(const FLatentActionInfo& Info) : ExecutionFunction(Info.ExecutionFunction) , OutputLink(Info.Linkage) , CallbackTarget(Info.CallbackTarget) {} virtual void UpdateOperation(FLatentResponse& Response) override { Response.FinishAndTriggerIf(bDone, ExecutionFunction, OutputLink, CallbackTarget); } }; FFixXmlLatent* Action = new FFixXmlLatent(LatentInfo); World->GetLatentActionManager().AddNewAction(LatentInfo.CallbackTarget, LatentInfo.UUID, Action); Async(EAsyncExecution::ThreadPool, [Action, xmlPath]() { FString xml; FFileHelper::LoadFileToString(xml, *xmlPath); xml.ReplaceInline(L">"); xml.ReplaceInline(L"]><", L"]<"); FFileHelper::SaveStringToFile(xml, *xmlPath); AsyncTask(ENamedThreads::GameThread, [Action]() { Action->bDone = true; }); }); }