Skip to main content

linjector-luau-script-ide

Open-source Luau/Lua script IDE and executor built as a Windows Forms C# application with Monaco editor integration

Jump to install

Source facts

Repository
reason-machines/trending-skills
Last source activity
May 4, 2026 at 07:34
Detected SKILL.md language
English
Stars
80
Forks
15

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
linjector-luau-script-ide
description
Open-source Luau/Lua script IDE and executor built as a Windows Forms C# application with Monaco editor integration
triggers
["help me build LInjector","how do I set up LInjector","LInjector Monaco editor integration","customize LInjector script executor","LInjector tab system","build Lua script IDE with C#","LInjector DLL injection","modify LInjector source code"]
# LInjector Luau Script IDE > Skill by [ara.so](https://ara.so) — Daily 2026 Skills collection. LInjector is an open-source Luau/Lua script IDE and executor built as a Windows Forms application in C# (.NET Framework 4.8, 32-bit). It features the Monaco editor (the same editor powering VS Code) with Luau syntax highlighting, an optimized multi-tab system, and DLL injection capabilities targeting the Roblox UWP client. > **Important Note (2026):** The UWP Version is patched due to Hyperion/Byfron anti-cheat. You can still build, modify, and extend LInjector by providing your own working DLL or injection method. --- ## Project Structure ``` LInjector/ ├── LInjector.sln # Visual Studio solution ├── LInjector/ │ ├── LInjector.csproj # Project file (.NET 4.8, x86) │ ├── Forms/ # Windows Forms UI │ │ ├── MainForm.cs # Main application window │ │ └── *.cs # Other form files │ ├── Classes/ # Core logic classes │ │ ├── Injector.cs # DLL injection logic │ │ ├── TabSystem.cs # Optimized tab management │ │ └── *.cs │ ├── Monaco/ # Monaco editor web assets │ │ ├── index.html # Editor host page │ │ └── *.js # Monaco JS files │ └── Resources/ # Embedded resources ``` --- ## Build Requirements - **Visual Studio 2022** - **.NET Framework 4.8** SDK - **Target Platform:** x86 (32-bit) - **Configuration:** Release --- ## How to Build ```bash # 1. Clone the repository git clone https://github.com/WeritoP/LInjector-FORKED- cd LInjector-FORKED- # 2. Open in Visual Studio 2022 start LInjector.sln # 3. Set build configuration in VS: # Configuration: Release # Platform: x86 # 4. Build via keyboard shortcut # CTRL + SHIFT + B # OR via CLI with MSBuild msbuild LInjector.sln /p:Configuration=Release /p:Platform=x86 ``` > **Important:** Always compile before editing Forms. If you open a Form before compiling, Visual Studio's designer will fail. If this happens, restart Visual Studio. --- ## Monaco Editor Integration LInjector embeds Monaco in a `WebBrowser` or `WebView2` control. The editor is hosted in a local HTML file. ### Accessing the Monaco Editor from C# ```csharp // In your Form class — get script content from Monaco private string GetEditorContent() { // Execute JavaScript to retrieve editor value object result = webBrowser.Document.InvokeScript( "eval", new object[] { "monaco.editor.getModels()[0].getValue()" } ); return result?.ToString() ?? string.Empty; } // Set content in Monaco editor private void SetEditorContent(string script) { string escaped = script .Replace("\\", "\\\\") .Replace("'", "\\'") .Replace("\n", "\\n") .Replace("\r", "\\r"); webBrowser.Document.InvokeScript( "eval", new object[] { $"monaco.editor.getModels()[0].setValue('{escaped}')" } ); } ``` ### Monaco Editor Host HTML (Monaco/index.html pattern) ```html <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <style> html, body, #container { width: 100%; height: 100%; margin: 0; padding: 0; overflow: hidden; background: #1e1e1e; } </style> </head> <body> <div id="container"></div> <script src="monaco/min/vs/loader.js"></script> <script> require.config({ paths: { 'vs': 'monaco/min/vs' } }); require(['vs/editor/editor.main'], function () { window.editor = monaco.editor.create( document.getElementById('container'), { value: '-- LInjector Script\n', language: 'lua', theme: 'vs-dark', fontSize: 14, automaticLayout: true, minimap: { enabled: false }, scrollBeyondLastLine: false } ); }); // Helper functions callable from C# function getScript() { return window.editor.getValue(); } function setScript(val) { window.editor.setValue(val); } function clearScript() { window.editor.setValue(''); } </script> </body> </html> ``` --- ## Tab System LInjector's tab system is optimized to cap RAM usage at ~1 GB even with 40+ tabs open. ```csharp // TabSystem.cs — Example pattern for managing script tabs public class ScriptTab { public string Name { get; set; } public string Content { get; set; } public TabPage TabPage { get; set; } public ScriptTab(string name, string content = "") { Name = name; Content = content; } } public class TabManager { private readonly List<ScriptTab> _tabs = new List<ScriptTab>(); private readonly TabControl _tabControl; private int _activeIndex = 0; public TabManager(TabControl tabControl) { _tabControl = tabControl; } // Add a new script tab public void AddTab(string name = null) { int tabNumber = _tabs.Count + 1; string tabName = name ?? $"Script {tabNumber}"; var tab = new ScriptTab(tabName); var tabPage = new TabPage(tabName); tab.TabPage = tabPage; _tabs.Add(tab); _tabControl.TabPages.Add(tabPage); _tabControl.SelectedTab = tabPage; } // Remove tab by index public void RemoveTab(int index) { if (index < 0 || index >= _tabs.Count) return; _tabControl.TabPages.Remove(_tabs[index].TabPage); _tabs.RemoveAt(index); } // Save current tab content before switching public void SaveCurrentTabContent(string content) { if (_activeIndex >= 0 && _activeIndex < _tabs.Count) _tabs[_activeIndex].Content = content; } // Get content for a tab public string GetTabContent(int index) { if (index < 0 || index >= _tabs.Count) return string.Empty; return _tabs[index].Content; } } ``` --- ## DLL Injection (Custom Integration) Since the original injection target is patched, here is the pattern used to plug in your own DLL: ```csharp // Classes/Injector.cs — Pattern for DLL injection using System; using System.Diagnostics; using System.IO; using System.Runtime.InteropServices; public class Injector { [DllImport("kernel32.dll")] private static extern IntPtr OpenProcess(int access, bool inherit, int processId); [DllImport("kernel32.dll")] private static extern IntPtr VirtualAllocEx( IntPtr hProcess, IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect); [DllImport("kernel32.dll")] private static extern bool WriteProcessMemory( IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, uint nSize, out UIntPtr lpNumberOfBytesWritten); [DllImport("kernel32.dll")] private static extern IntPtr CreateRemoteThread( IntPtr hProcess, IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId); [DllImport("kernel32.dll", CharSet = CharSet.Auto)] private static extern IntPtr GetModuleHandle(string lpModuleName); [DllImport("kernel32.dll", CharSet = CharSet.Ansi, ExactSpelling = true)] private static extern IntPtr GetProcAddress(IntPtr hModule, string procName); private const int PROCESS_ALL_ACCESS = 0x1F0FFF; private const uint MEM_COMMIT = 0x1000; private const uint MEM_RESERVE = 0x2000; private const uint PAGE_READWRITE = 0x04; /// <summary> /// Inject a DLL into a target process by name. /// Returns true on success. /// </summary> public static bool InjectDll(string processName, string dllPath) { if (!File.Exists(dllPath)) throw new FileNotFoundException($"DLL not found: {dllPath}"); Process[] procs = Process.GetProcessesByName(processName); if (procs.Length == 0) throw new Exception($"Process '{processName}' not found."); Process target = procs[0]; IntPtr hProcess = OpenProcess(PROCESS_ALL_ACCESS, false, target.Id); if (hProcess == IntPtr.Zero) throw new Exception("Failed to open process."); byte[] dllBytes = System.Text.Encoding.ASCII.GetBytes(dllPath + "\0"); IntPtr allocMem = VirtualAllocEx( hProcess, IntPtr.Zero, (uint)dllBytes.Length, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); if (allocMem == IntPtr.Zero) throw new Exception("Memory allocation failed."); WriteProcessMemory(hProcess, allocMem, dllBytes, (uint)dllBytes.Length, out _); IntPtr loadLibAddr = GetProcAddress( GetModuleHandle("kernel32.dll"), "LoadLibraryA"); IntPtr thread = CreateRemoteThread( hProcess, IntPtr.Zero, 0, loadLibAddr, allocMem, 0, IntPtr.Zero); return thread != IntPtr.Zero; } } ``` ### Using the Injector from UI ```csharp // In MainForm.cs — Inject button click handler private async void btnInject_Click(object sender, EventArgs e) { string dllPath = Path.Combine( Application.StartupPath, "Modules", "your_dll.dll"); try { bool success = await Task.Run(() => Injector.InjectDll("RobloxPlayerBeta", dllPath)); lblStatus.Text = success ? "✔ Injected successfully" : "✘ Injection failed"; } catch (Exception ex) { MessageBox.Show($"Error: {ex.Message}", "LInjector", MessageBoxButtons.OK, MessageBoxIcon.Error); } } ``` --- ## Script Execution Pattern ```csharp // Execute the current script via the injected DLL's named pipe or HTTP endpoint private async void btnExecute_Click(object sender, EventArgs e) { string script = GetEditorContent(); if (string.IsNullOrWhiteSpace(script)) return; try { await ExecuteScriptAsync(script); lblStatus.Text = "Script executed."; } catch (Exception ex) { lblStatus.Text = $"Error: {ex.Message}"; } } private async Task ExecuteScriptAsync(string script) { // Example: communicate with injected DLL via named pipe using var pipe = new System.IO.Pipes.NamedPipeClientStream( ".", "LInjectorPipe", System.IO.Pipes.PipeDirection.Out); await pipe.ConnectAsync(timeoutMilliseconds: 3000); byte[] data = System.Text.Encoding.UTF8.GetBytes(script); await pipe.WriteAsync(data, 0, data.Length); } ``` --- ## Script File Management ```csharp // Save script to file private void SaveScript(string content, string filePath = null) { if (filePath == null) { using var sfd = new SaveFileDialog { Filter = "Lua Files (*.lua)|*.lua|Text Files (*.txt)|*.txt|All Files (*.*)|*.*", DefaultExt = "lua", FileName = "script" }; if (sfd.ShowDialog() != DialogResult.OK) return; filePath = sfd.FileName; } File.WriteAllText(filePath, content, System.Text.Encoding.UTF8); } // Load script from file private string LoadScript() { using var ofd = new OpenFileDialog { Filter = "Lua Files (*.lua)|*.lua|Text Files (*.txt)|*.txt|All Files (*.*)|*.*" }; if (ofd.ShowDialog() != DialogResult.OK) return null; return File.ReadAllText(ofd.FileName, System.Text.Encoding.UTF8); } ``` --- ## Configuration Pattern LInjector uses app settings for persistent configuration: ```csharp // Reading/writing settings (App.config / user settings) // In Settings.settings, add keys: Theme, FontSize, AutoInject // Read string theme = Properties.Settings.Default.Theme ?? "vs-dark"; int fontSize = Properties.Settings.Default.FontSize > 0 ? Properties.Settings.Default.FontSize : 14; // Write and save Properties.Settings.Default.Theme = "vs-dark"; Properties.Settings.Default.FontSize = 16; Properties.Settings.Default.Save(); // Apply theme to Monaco from C# private void ApplyEditorTheme(string theme) { webBrowser.Document.InvokeScript( "eval", new object[] { $"monaco.editor.setTheme('{theme}')" } ); } ``` --- ## Common Customization Patterns ### Change Target Process ```csharp // Replace "RobloxPlayerBeta" with your target process name private const string TARGET_PROCESS = "RobloxPlayerBeta"; // or for UWP: // private const string TARGET_PROCESS = "Windows10Universal"; ``` ### Add Custom Luau Autocomplete to Monaco ```javascript // In Monaco/index.html — register Luau-specific completions monaco.languages.registerCompletionItemProvider('lua', { provideCompletionItems: function(model, position) { return { suggestions: [ { label: 'game', kind: monaco.languages.CompletionItemKind.Variable, insertText: 'game', documentation: 'The root DataModel instance' }, { label: 'workspace', kind: monaco.languages.CompletionItemKind.Variable, insertText: 'workspace', documentation: 'Alias for game.Workspace' }, { label: 'loadstring',
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub