flockbay 0.10.17 → 0.10.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -21,7 +21,7 @@ import net from 'node:net';
21
21
  import { spawn as spawn$1 } from 'node:child_process';
22
22
 
23
23
  var name = "flockbay";
24
- var version = "0.10.17";
24
+ var version = "0.10.20";
25
25
  var description = "Flockbay CLI (local agent + daemon)";
26
26
  var author = "Eduardo Orellana";
27
27
  var license = "UNLICENSED";
@@ -245,7 +245,7 @@ class Configuration {
245
245
  }
246
246
  } catch {
247
247
  }
248
- const rawServerUrl = process.env.FLOCKBAY_SERVER_URL || persistedSettings?.serverUrl || "https://api-internal.flockbay.com";
248
+ const rawServerUrl = process.env.FLOCKBAY_SERVER_URL || persistedSettings?.serverUrl || "https://api.flockbay.com";
249
249
  this.serverUrl = normalizeServerUrlForNode(rawServerUrl);
250
250
  this.webappUrl = process.env.FLOCKBAY_WEBAPP_URL || persistedSettings?.webappUrl || "https://flockbay.com";
251
251
  this.isExperimentalEnabled = ["true", "1", "yes"].includes(
@@ -3774,4 +3774,4 @@ const RawJSONLinesSchema = z$1.discriminatedUnion("type", [
3774
3774
  }).passthrough()
3775
3775
  ]);
3776
3776
 
3777
- export { ApiClient as A, RawJSONLinesSchema as R, ApiSessionClient as a, packageJson as b, configuration as c, backoff as d, delay as e, readDaemonState as f, clearDaemonState as g, readCredentials as h, unrealMcpPythonDir as i, acquireDaemonLock as j, writeDaemonState as k, logger as l, ApiMachineClient as m, releaseDaemonLock as n, clearCredentials as o, projectPath as p, clearMachineId as q, readSettings as r, sendUnrealMcpTcpCommand as s, installUnrealMcpPluginToEngine as t, updateSettings as u, getLatestDaemonLog as v, writeCredentials as w };
3777
+ export { ApiClient as A, RawJSONLinesSchema as R, ApiSessionClient as a, packageJson as b, configuration as c, backoff as d, delay as e, readDaemonState as f, clearDaemonState as g, readCredentials as h, unrealMcpPythonDir as i, acquireDaemonLock as j, writeDaemonState as k, logger as l, ApiMachineClient as m, releaseDaemonLock as n, clearCredentials as o, projectPath as p, clearMachineId as q, readSettings as r, sendUnrealMcpTcpCommand as s, installUnrealMcpPluginToEngine as t, updateSettings as u, getLatestDaemonLog as v, writeCredentials as w, normalizeServerUrlForNode as x };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "flockbay",
3
- "version": "0.10.17",
3
+ "version": "0.10.20",
4
4
  "description": "Flockbay CLI (local agent + daemon)",
5
5
  "author": "Eduardo Orellana",
6
6
  "license": "UNLICENSED",
@@ -360,6 +360,16 @@ static TMap<FString, FUnrealMCPCommandSchema> BuildSchemas()
360
360
 
361
361
  {
362
362
  TArray<FUnrealMCPParamSchema> P;
363
+ AddEnumParam(
364
+ P,
365
+ TEXT("source"),
366
+ false,
367
+ TEXT("Capture source: auto picks PIE when playing, otherwise editor viewport."),
368
+ {TEXT("auto"), TEXT("pie"), TEXT("editor")},
369
+ {TEXT("mode")},
370
+ {{TEXT("play"), TEXT("pie")}, {TEXT("game"), TEXT("pie")}, {TEXT("viewport"), TEXT("editor")}});
371
+ AddNumberParam(P, TEXT("width"), false, TEXT("Optional output width (defaults to game viewport size or 1280)."), {TEXT("resX"), TEXT("w")});
372
+ AddNumberParam(P, TEXT("height"), false, TEXT("Optional output height (defaults to game viewport size or 720)."), {TEXT("resY"), TEXT("h")});
363
373
  AddStringParam(P, TEXT("filepath"), false, TEXT("Output path. If omitted, defaults to <Project>/Saved/Screenshots/Flockbay/."), {TEXT("path"), TEXT("file_path")});
364
374
  AddStringParam(P, TEXT("filename"), false, TEXT("Optional file name (auto-generated if omitted)."));
365
375
  AddSimpleCommand(TEXT("take_screenshot"), TEXT("Take Screenshot"), TEXT("Capture a screenshot from the editor or PIE."), P, nullptr);
@@ -49,6 +49,10 @@
49
49
  #include "Engine/EngineTypes.h"
50
50
  #include "GameFramework/PlayerController.h"
51
51
  #include "Camera/PlayerCameraManager.h"
52
+ #include "Engine/LocalPlayer.h"
53
+ #include "Engine/SceneCapture2D.h"
54
+ #include "Components/SceneCaptureComponent2D.h"
55
+ #include "Engine/TextureRenderTarget2D.h"
52
56
 
53
57
  FUnrealMCPEditorCommands::FUnrealMCPEditorCommands()
54
58
  {
@@ -2295,26 +2299,193 @@ TSharedPtr<FJsonObject> FUnrealMCPEditorCommands::HandleTakeScreenshot(const TSh
2295
2299
  FilePath += TEXT(".png");
2296
2300
  }
2297
2301
 
2298
- // Get the active viewport
2302
+ FString Source = TEXT("auto");
2303
+ if (Params->TryGetStringField(TEXT("source"), Source))
2304
+ {
2305
+ Source = Source.TrimStartAndEnd().ToLower();
2306
+ if (Source.IsEmpty())
2307
+ {
2308
+ Source = TEXT("auto");
2309
+ }
2310
+ }
2311
+
2312
+ double WidthNum = 0.0;
2313
+ double HeightNum = 0.0;
2314
+ int32 RequestedWidth = 0;
2315
+ int32 RequestedHeight = 0;
2316
+ if (Params->TryGetNumberField(TEXT("width"), WidthNum))
2317
+ {
2318
+ RequestedWidth = static_cast<int32>(WidthNum);
2319
+ }
2320
+ else if (Params->TryGetNumberField(TEXT("resX"), WidthNum))
2321
+ {
2322
+ RequestedWidth = static_cast<int32>(WidthNum);
2323
+ }
2324
+
2325
+ if (Params->TryGetNumberField(TEXT("height"), HeightNum))
2326
+ {
2327
+ RequestedHeight = static_cast<int32>(HeightNum);
2328
+ }
2329
+ else if (Params->TryGetNumberField(TEXT("resY"), HeightNum))
2330
+ {
2331
+ RequestedHeight = static_cast<int32>(HeightNum);
2332
+ }
2333
+
2334
+ const bool bPieRunning = (GEditor && GEditor->PlayWorld);
2335
+ const bool bWantsPie = (Source == TEXT("pie"));
2336
+ const bool bWantsEditor = (Source == TEXT("editor"));
2337
+ const bool bAuto = (Source == TEXT("auto"));
2338
+ if (!bWantsPie && !bWantsEditor && !bAuto)
2339
+ {
2340
+ return FUnrealMCPCommonUtils::CreateErrorResponse(FString::Printf(TEXT("Invalid screenshot source: %s (expected: auto|pie|editor)"), *Source));
2341
+ }
2342
+
2343
+ const bool bUsePieCapture = bWantsPie || (bAuto && bPieRunning);
2344
+ if (bUsePieCapture)
2345
+ {
2346
+ if (!bPieRunning)
2347
+ {
2348
+ return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("PIE is not running (PlayWorld is null)."));
2349
+ }
2350
+
2351
+ UWorld* PlayWorld = GEditor->PlayWorld;
2352
+ APlayerController* PC = PlayWorld ? PlayWorld->GetFirstPlayerController() : nullptr;
2353
+ if (!PC)
2354
+ {
2355
+ return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Failed to find PlayerController in PlayWorld."));
2356
+ }
2357
+
2358
+ APlayerCameraManager* PCM = PC->PlayerCameraManager;
2359
+ if (!PCM)
2360
+ {
2361
+ return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Failed to find PlayerCameraManager (PlayerController.PlayerCameraManager is null)."));
2362
+ }
2363
+
2364
+ int32 Width = RequestedWidth;
2365
+ int32 Height = RequestedHeight;
2366
+ if (Width <= 0 || Height <= 0)
2367
+ {
2368
+ if (ULocalPlayer* LocalPlayer = PC->GetLocalPlayer())
2369
+ {
2370
+ if (LocalPlayer->ViewportClient && LocalPlayer->ViewportClient->Viewport)
2371
+ {
2372
+ const FIntPoint ViewSize = LocalPlayer->ViewportClient->Viewport->GetSizeXY();
2373
+ if (ViewSize.X > 0 && ViewSize.Y > 0)
2374
+ {
2375
+ if (Width <= 0)
2376
+ {
2377
+ Width = ViewSize.X;
2378
+ }
2379
+ if (Height <= 0)
2380
+ {
2381
+ Height = ViewSize.Y;
2382
+ }
2383
+ }
2384
+ }
2385
+ }
2386
+ }
2387
+
2388
+ if (Width <= 0 || Height <= 0)
2389
+ {
2390
+ Width = 1280;
2391
+ Height = 720;
2392
+ }
2393
+
2394
+ const FVector CamLocation = PCM->GetCameraLocation();
2395
+ const FRotator CamRotation = PCM->GetCameraRotation();
2396
+
2397
+ FActorSpawnParameters SpawnParams;
2398
+ SpawnParams.ObjectFlags |= RF_Transient;
2399
+ SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
2400
+
2401
+ ASceneCapture2D* CaptureActor = PlayWorld->SpawnActor<ASceneCapture2D>(ASceneCapture2D::StaticClass(), CamLocation, CamRotation, SpawnParams);
2402
+ if (!CaptureActor)
2403
+ {
2404
+ return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Failed to spawn ASceneCapture2D for PIE screenshot capture."));
2405
+ }
2406
+
2407
+ USceneCaptureComponent2D* CaptureComp = CaptureActor->GetCaptureComponent2D();
2408
+ if (!CaptureComp)
2409
+ {
2410
+ CaptureActor->Destroy();
2411
+ return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Failed to get SceneCaptureComponent2D from ASceneCapture2D."));
2412
+ }
2413
+
2414
+ UTextureRenderTarget2D* RenderTarget = NewObject<UTextureRenderTarget2D>(CaptureComp);
2415
+ if (!RenderTarget)
2416
+ {
2417
+ CaptureActor->Destroy();
2418
+ return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Failed to create UTextureRenderTarget2D."));
2419
+ }
2420
+ RenderTarget->InitCustomFormat(Width, Height, PF_B8G8R8A8, /*bForceLinearGamma*/ false);
2421
+ RenderTarget->ClearColor = FLinearColor::Black;
2422
+ RenderTarget->UpdateResourceImmediate(true);
2423
+
2424
+ CaptureComp->TextureTarget = RenderTarget;
2425
+ CaptureComp->bCaptureEveryFrame = false;
2426
+ CaptureComp->bCaptureOnMovement = false;
2427
+ CaptureComp->CaptureSource = ESceneCaptureSource::SCS_FinalColorLDR;
2428
+ CaptureComp->FOVAngle = PCM->GetFOVAngle();
2429
+
2430
+ CaptureComp->CaptureScene();
2431
+
2432
+ FTextureRenderTargetResource* RenderResource = RenderTarget->GameThread_GetRenderTargetResource();
2433
+ if (!RenderResource)
2434
+ {
2435
+ CaptureActor->Destroy();
2436
+ return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Failed to get render target resource for PIE screenshot capture."));
2437
+ }
2438
+
2439
+ TArray<FColor> Bitmap;
2440
+ if (!RenderResource->ReadPixels(Bitmap))
2441
+ {
2442
+ CaptureActor->Destroy();
2443
+ return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Failed to read pixels from PIE render target."));
2444
+ }
2445
+
2446
+ CaptureActor->Destroy();
2447
+
2448
+ TArray<uint8> CompressedBitmap;
2449
+ FImageUtils::CompressImageArray(Width, Height, Bitmap, CompressedBitmap);
2450
+ if (!FFileHelper::SaveArrayToFile(CompressedBitmap, *FilePath))
2451
+ {
2452
+ return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Failed to save screenshot PNG to disk."));
2453
+ }
2454
+
2455
+ TSharedPtr<FJsonObject> ResultObj = MakeShared<FJsonObject>();
2456
+ ResultObj->SetStringField(TEXT("filepath"), FilePath);
2457
+ ResultObj->SetStringField(TEXT("source"), TEXT("pie"));
2458
+ ResultObj->SetNumberField(TEXT("width"), Width);
2459
+ ResultObj->SetNumberField(TEXT("height"), Height);
2460
+ return ResultObj;
2461
+ }
2462
+
2463
+ // Editor viewport screenshot (can be stale if the OS window is not focused; prefer PIE capture when playing).
2299
2464
  if (GEditor && GEditor->GetActiveViewport())
2300
2465
  {
2466
+ GEditor->RedrawAllViewports(/*bInvalidateHitProxies*/ true);
2467
+
2301
2468
  FViewport* Viewport = GEditor->GetActiveViewport();
2469
+ const FIntPoint ViewSize = Viewport->GetSizeXY();
2302
2470
  TArray<FColor> Bitmap;
2303
- FIntRect ViewportRect(0, 0, Viewport->GetSizeXY().X, Viewport->GetSizeXY().Y);
2304
-
2471
+ FIntRect ViewportRect(0, 0, ViewSize.X, ViewSize.Y);
2472
+
2305
2473
  if (Viewport->ReadPixels(Bitmap, FReadSurfaceDataFlags(), ViewportRect))
2306
2474
  {
2307
2475
  TArray<uint8> CompressedBitmap;
2308
- FImageUtils::CompressImageArray(Viewport->GetSizeXY().X, Viewport->GetSizeXY().Y, Bitmap, CompressedBitmap);
2309
-
2476
+ FImageUtils::CompressImageArray(ViewSize.X, ViewSize.Y, Bitmap, CompressedBitmap);
2477
+
2310
2478
  if (FFileHelper::SaveArrayToFile(CompressedBitmap, *FilePath))
2311
2479
  {
2312
2480
  TSharedPtr<FJsonObject> ResultObj = MakeShared<FJsonObject>();
2313
2481
  ResultObj->SetStringField(TEXT("filepath"), FilePath);
2482
+ ResultObj->SetStringField(TEXT("source"), TEXT("editor"));
2483
+ ResultObj->SetNumberField(TEXT("width"), ViewSize.X);
2484
+ ResultObj->SetNumberField(TEXT("height"), ViewSize.Y);
2314
2485
  return ResultObj;
2315
2486
  }
2316
2487
  }
2317
2488
  }
2318
-
2319
- return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Failed to take screenshot"));
2489
+
2490
+ return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Failed to take screenshot."));
2320
2491
  }