Mukund Pareek / mukund pareek
  • Projects
  • Blog
  • Notes
  • Resume

MP Mukund Pareek

Unreal Engine C++ Developer

  • Projects
  • Blog
  • Notes
  • Resume

© 2026 Mukund Pareek. Built with Next.js and Tailwind CSS.

Projects
Repository Commits
February 20, 2026
Editor Tools

Quick Facts

Engine
Unreal Engine 5
Language
C++
System
Unreal Editor API
Type
Editor Tools
Status
Prototype
AI Chatbot Editor Plugin thumbnail
Editor Tools

AI Chatbot Editor Plugin

UE5 C++ editor plugin that registers a toolbar command, opens a standalone Slate chat window, sends prompts to the Mistral API, and renders the parsed grid response as interactive Slate buttons - all inside the editor.

unreal
editor-plugin
c++
slate
http

Project at a Glance

This repository is a UE5 C++ project (JoinTask) hosting an editor plugin (MinesButton). The plugin registers a command in the Unreal Editor toolbar and opens a standalone Slate window with a chat interface. The interface submits a natural-language prompt to Mistral's chat API, parses the response JSON, and renders the extracted grid as a clickable Slate button grid.

End-to-end flow:

  1. Editor toolbar/menu command launches a custom Slate window
  2. User types a prompt and presses Enter
  3. Plugin sends HTTP POST to Mistral /v1/chat/completions
  4. Response JSON is parsed (choices[0].message.content)
  5. Extracted grid text is displayed; Play generates an interactive Slate button grid

Quick Facts

FieldValue
EngineUnreal Engine 5.3
LanguageC++ (Unreal C++ + Slate)
Primary RuntimeEditor plugin - toolbar + menu command
Secondary RuntimeIn-game HUD-backed Slate injection (prepared, not default flow)
AI ProviderMistral Chat Completions API
UI StackSlate - no UMG in the main chat widget path
Build SystemUnreal Build Tool (.Build.cs, Target.cs)
Project TypeUE C++ project (JoinTask) with custom plugin (MinesButton)
MaturityPrototype / experimental

Repository Structure

  • Source/JoinTask/* - base game module and targets (minimal gameplay logic)
  • Plugins/MinesButton/Source/MinesButton/Private/
    • MinesButton.cpp - plugin module lifecycle, command binding, menu/toolbar integration, window spawn
    • SChatWidget.cpp - Slate chat widget, response processing, grid rendering
    • MinesweeperAIRequest.cpp - HTTP request construction and callback wiring
    • MinesButtonStyle.cpp + MinesButtonCommands.cpp - command and icon/style registration
    • WindowHUD.cpp - alternate HUD path for viewport widget injection
  • Config/*.ini - UE project + rendering/input/editor/game config
  • JoinTask.uproject + MinesButton.uplugin - module/plugin metadata and load topology

Runtime Architecture

Runtime architecture - editor command through HTTP exchange to Slate grid rendering.

Core Components

FMinesButtonModule - Editor integration + window orchestration

Plugin entrypoint and Editor UI integration.

  • Initializes style and command registration in StartupModule
  • Binds command execution to PluginButtonClicked
  • Extends LevelEditor.MainMenu.Window and LevelEditor.LevelEditorToolBar.PlayToolBar
  • Spawns a dedicated SWindow containing SChatWidget
  • Maintains widget/window references to prevent duplicates and raise existing window to front

Design note: Module type is Editor in .uplugin - correctly scoped for editor-time workflows only.

SChatWidget - Presentation + interaction + response shaping

User-facing Slate UI and main orchestration node.

UI tree: prompt label → SEditableTextBox → response STextBlock → Play button (disabled until response) → SVerticalBox grid container.

  • On Enter: forwards raw prompt to AI request object
  • On response: parses JSON, extracts model content field, applies post-processing to strip explanatory text, enables grid action
  • On Play: tokenizes multiline response into rows/cells, builds SHorizontalBox rows filled with per-cell SButton widgets

Note: Grid generation is presentational. No game-state model for reveal/flag/win-loss rules exists yet.

UMinesweeperAIRequest - Network adapter

Thin HTTP call lifecycle wrapper.

  • Builds POST request to https://api.mistral.ai/v1/chat/completions
  • Sets Content-Type and bearer authorization headers
  • Sends user prompt as JSON body
  • Returns raw response body via delegate to SChatWidget

Acts as a gateway only - semantic parsing is left to SChatWidget.

FMinesButtonCommands + FMinesButtonStyle - Command and style system

  • Defines the PluginAction command exposed to menu and toolbar
  • Registers style set and SVG icon from plugin Resources directory

AWindowHUD - Alternate runtime hook

Creates and attaches SChatWidget to GameViewport via SWeakWidget on BeginPlay. Present as a second integration path; the primary feature path is the editor-window flow.

Build & Module Topology

Host project (JoinTask)

  • Standard game module (IMPLEMENT_PRIMARY_GAME_MODULE)
  • Targets: JoinTaskTarget (Game) + JoinTaskEditorTarget (Editor)

Plugin module (MinesButton)

  • Type: Editor, loaded in default phase
  • Dependencies: Core + Slate/SlateCore + ToolMenus/UnrealEd/EditorFramework + HTTP + Json + JsonUtilities

This dependency shape matches the plugin's responsibilities: Editor extensibility, network, JSON, and custom Slate UI.

Code Snippet

cppMinesweeperAIRequest.cpp
void UMinesweeperAIRequest::SendAIRequest(const FString& UserPrompt)
{
  TSharedRef<IHttpRequest, ESPMode::ThreadSafe> Request =
      FHttpModule::Get().CreateRequest();

  Request->SetVerb(TEXT("POST"));
  Request->SetURL(TEXT("https://api.mistral.ai/v1/chat/completions"));
  Request->SetHeader(TEXT("Content-Type"), TEXT("application/json"));
  Request->SetHeader(TEXT("Authorization"),
      FString::Printf(TEXT("Bearer %s"), *BearerToken));  // TODO: read from config

  // NOTE: Prompt is interpolated directly - minimal escaping applied.
  // Production path: use FJsonWriter to build payload safely.
  const FString Body = FString::Printf(
      TEXT("{\"model\":\"mistral-small\","
           "\"messages\":[{\"role\":\"user\",\"content\":\"%s\"}]}"),
      *UserPrompt);

  Request->SetContentAsString(Body);
  Request->OnProcessRequestComplete().BindUObject(
      this, &UMinesweeperAIRequest::OnResponseReceived);
  Request->ProcessRequest();
}

void UMinesweeperAIRequest::OnResponseReceived(
  FHttpRequestPtr Request,
  FHttpResponsePtr Response,
  bool bSuccess)
{
  if (!bSuccess || !Response.IsValid())
  {
      OnAIResponseReceived.ExecuteIfBound(TEXT("Error: request failed."));
      return;
  }

  TSharedPtr<FJsonObject> JsonObject;
  TSharedRef<TJsonReader<>> Reader =
      TJsonReaderFactory<>::Create(Response->GetContentAsString());

  if (FJsonSerializer::Deserialize(Reader, JsonObject))
  {
      const TArray<TSharedPtr<FJsonValue>>* Choices;
      if (JsonObject->TryGetArrayField(TEXT("choices"), Choices) && Choices->Num() > 0)
      {
          const FString Content = (*Choices)[0]
              ->AsObject()->GetObjectField(TEXT("message"))
              ->GetStringField(TEXT("content"));

          OnAIResponseReceived.ExecuteIfBound(Content);
          return;
      }
  }

  OnAIResponseReceived.ExecuteIfBound(TEXT("Error: could not parse response."));
}

Data & Control Flow

  1. Module startup - StartupModule initializes style and commands; UToolMenus::RegisterStartupCallback defers menu extension until menus are ready
  2. User opens plugin - PluginButtonClicked creates a fresh SWindow + SChatWidget or raises the existing one
  3. Prompt submission - OnChatSubmitted fires on ETextCommit::OnEnter and forwards text to UMinesweeperAIRequest
  4. HTTP exchange - POST request emitted to Mistral; OnProcessRequestComplete callback fires with raw JSON body
  5. Response parsing - HandleAIResponse deserializes JSON, extracts choices[0].message.content, strips explanatory phrases, enables Play
  6. Grid rendering - GenerateMinesweeperBoard tokenizes response by newlines and spaces; each token becomes an SButton in a visual grid

Video Demo

Demo: triggering the toolbar command, entering a prompt, receiving a Mistral response, and generating the Slate button grid.

Strengths

  • Clean separation between Editor module/bootstrap, UI widget orchestration, and network transport
  • Uses Unreal-native extension points - ToolMenus, command lists, Slate style registry
  • Fast prototype path from prompt to rendered UI with minimal moving parts
  • Mermaid runtime architecture is easy to follow and extend

Technical Risks

RiskDetail
Hardcoded bearer tokenToken is set directly in C++ source; must move to per-user config or env-driven retrieval
Threading assumptionsUI updated directly in callback path; explicit game-thread marshaling (AsyncTask) would harden safety
Response contract fragilityAssumes exact JSON schema; no fallback for schema drift or unexpected model formatting
Prompt injection surfaceUser text interpolated into JSON via FString::Printf - minimal escaping applied
No timeout / retryNetwork errors collapse to a generic failure string
No gameplay modelGenerated grid is visual only; minesweeper mechanics (reveal, flag, win/loss) not implemented

Suggested Evolution Path

  1. Security - Remove hardcoded token; read from DefaultEditorPerProjectUserSettings.ini (per-user, gitignored) or environment variable
  2. Protocol robustness - Build request payload with FJsonWriter; add schema validation with graceful error UI
  3. UX - Scrollable chat history, loading indicator, input disabled while request in flight, retry action
  4. Game logic - Introduce board model (cells, mines, reveal state, adjacency counts) decoupled from rendering
  5. Integration clarity - Decide between editor-only tool vs runtime HUD feature; remove dead path or formally support both modes
  6. Testability - Extract JSON parser and grid formatter into pure utility functions for isolated unit tests

Bottom Line

A prototype-grade Unreal Editor plugin demonstrating an end-to-end AI-assisted workflow in Slate: command registration, custom window lifecycle, external API call via HTTP Module, JSON parsing, and dynamic widget rendering. The module → widget → request adapter shape is clean, but the path to production tooling requires hardening around secrets management, error handling, and domain modeling.