/* Luma Tools — Online installer (auto-update from Explorer server) ------------------------------------------------------------ One-shot per workstation: 1. In 3ds Max: Scripting → Run Script… → this install_online.ms 2. Tools sync into %LOCALAPPDATA%\LUMA\luma-explorer\ 3. Startup hook re-checks VERSION on each Max launch Default CDN: https://explorer.lumainteractive.online/luma-explorer/toolkit_cdn.php?path=… Offline / air-gapped: use install.ms from a local zip instead. ------------------------------------------------------------ */ -- Public reverse-proxy root (same host as admin_dashboard.php). Do not point at LAN. global lumaRemoteBase = "https://explorer.lumainteractive.online/luma-explorer/" fn lumaOnlineBootstrapRemoteMs = ( -- Minimal download of luma_remote.ms before full sync helpers exist. -- Prefer powershell.exe (firewall-safe); fall back to Max WebClient. local env = dotNetClass "System.Environment" local folderEnum = dotNetClass "System.Environment+SpecialFolder" local localApp = env.GetFolderPath folderEnum.LocalApplicationData local root = (localApp as string) + "\\LUMA\\luma-explorer\\" makeDir (root + "lib\\") all:true global lumaRootDir = root local base = lumaRemoteBase as string if base.count > 0 do ( local last = substring base base.count 1 if last != "/" do base += "/" ) local url = base + "toolkit_cdn.php?path=lib/luma_remote.ms" local dest = root + "lib\\luma_remote.ms" local downloaded = false try ( if doesFileExist dest do try (deleteFile dest) catch () local psExe = undefined local candidates = #( (sysInfo.windowsdir + "\\System32\\WindowsPowerShell\\v1.0\\powershell.exe"), "powershell.exe" ) for c in candidates while psExe == undefined do ( if c != undefined and c != "" do ( if findString c "\\" != undefined then ( if doesFileExist c do psExe = c ) else ( psExe = c ) ) ) if psExe != undefined do ( local exitCode = -1 local cmd = "-NoProfile -ExecutionPolicy Bypass -Command \"Invoke-WebRequest -Uri '" + url + "' -OutFile '" + dest + "' -UseBasicParsing -TimeoutSec 60\"" hiddenDOSCommand (psExe + " " + cmd) exitCode:&exitCode if doesFileExist dest do downloaded = true ) ) catch () if not downloaded do ( try ( local spm = dotNetClass "System.Net.ServicePointManager" local proto = dotNetClass "System.Net.SecurityProtocolType" spm.SecurityProtocol = proto.Tls12 ) catch () try ( if doesFileExist dest do try (deleteFile dest) catch () local wc = dotNetObject "System.Net.WebClient" wc.DownloadFile url dest try (wc.Dispose()) catch () if doesFileExist dest do downloaded = true ) catch ( messageBox ("Could not download luma_remote.ms from the LUMA Explorer server.\n\n" + (getCurrentException() as string) + "\n\nURL:\n" + url) title:"Luma Tools" return false ) ) if not doesFileExist dest do ( messageBox ("Download finished but file missing:\n" + dest + "\n\nURL:\n" + url) title:"Luma Tools" return false ) try ( fileIn dest ) catch ( messageBox ("Failed to load luma_remote.ms:\n" + (getCurrentException() as string)) title:"Luma Tools" return false ) true ) fn lumaInstallOnlineSuite = ( if not (lumaOnlineBootstrapRemoteMs()) do return false if lumaSyncFromRemote == undefined do ( messageBox "lumaSyncFromRemote was not defined after loading luma_remote.ms." title:"Luma Tools" return false ) local synced = lumaSyncFromRemote quiet:false force:true if not synced do return false local packageRoot = lumaCacheRoot() if packageRoot == undefined do ( messageBox "Cache path unknown after sync." title:"Luma Tools" return false ) global lumaRootDir = packageRoot try ( local companionLib = packageRoot + "lib\\luma_companion_install.ms" if doesFileExist companionLib do fileIn companionLib if lumaSyncGoogleCaptureCompanion != undefined do lumaSyncGoogleCaptureCompanion packageRoot ) catch ( format "Luma Tools: companion sync skipped: %\n" (getCurrentException()) ) local corePath = packageRoot + "lib\\luma_core.ms" local menuPath = packageRoot + "lib\\luma_menu.ms" local macrosSrc = packageRoot + "macros\\luma_macros.mcr" if not doesFileExist corePath do ( messageBox ("Missing after sync:\n" + corePath) title:"Luma Tools" return false ) if not doesFileExist menuPath do ( messageBox ("Missing after sync:\n" + menuPath) title:"Luma Tools" return false ) try (fileIn corePath) catch ( messageBox ("Failed to load luma_core.ms:\n" + (getCurrentException() as string)) title:"Luma Tools" return false ) try (fileIn menuPath) catch ( messageBox ("Failed to load luma_menu.ms:\n" + (getCurrentException() as string)) title:"Luma Tools" return false ) local startupDir = (getDir #userStartupScripts) + "\\" makeDir startupDir all:true local startupFile = startupDir + "luma_explorer_startup.ms" local startupBody = "" startupBody += "/* Auto-generated by Luma Tools install_online.ms */\n" startupBody += "global lumaRemoteBase = @\"" + (lumaRemoteBase as string) + "\"\n" startupBody += "global lumaRootDir = @\"" + (packageRoot as string) + "\"\n" startupBody += "(\n" startupBody += " local remoteMs = lumaRootDir + \"lib\\\\luma_remote.ms\"\n" startupBody += " if doesFileExist remoteMs then (\n" startupBody += " try (fileIn remoteMs) catch ()\n" startupBody += " if lumaSyncFromRemote != undefined do (\n" startupBody += " try (lumaSyncFromRemote quiet:true force:false) catch (\n" startupBody += " format \"Luma Tools: sync skipped (%)\\n\" (getCurrentException())\n" startupBody += " )\n" startupBody += " )\n" startupBody += " )\n" startupBody += " if doesFileExist (lumaRootDir + \"lib\\\\luma_core.ms\") then (\n" startupBody += " fileIn (lumaRootDir + \"lib\\\\luma_core.ms\")\n" startupBody += " fileIn (lumaRootDir + \"lib\\\\luma_menu.ms\")\n" startupBody += " local mcr = lumaRootDir + \"macros\\\\luma_macros.mcr\"\n" startupBody += " if doesFileExist mcr do fileIn mcr\n" startupBody += " if lumaEnsureMenu != undefined then lumaEnsureMenu force:true\n" startupBody += " else if lumaRegisterMenu != undefined do lumaRegisterMenu()\n" startupBody += " format \"Luma Tools ready (%).\\n\" lumaSuiteVersion\n" startupBody += " ) else (\n" startupBody += " format \"Luma Tools: package missing at %\\n\" lumaRootDir\n" startupBody += " )\n" startupBody += ")\n" local f = createFile startupFile if f == undefined then ( messageBox ("Could not write startup script:\n" + startupFile) title:"Luma Tools" return false ) format "%" startupBody to:f close f local userMacroDir = (getDir #userMacros) + "\\" makeDir userMacroDir all:true local macrosDst = userMacroDir + "luma_macros.mcr" if doesFileExist macrosSrc do ( if doesFileExist macrosDst do try (deleteFile macrosDst) catch () try (copyFile macrosSrc macrosDst) catch () try (fileIn macrosDst) catch () try (fileIn macrosSrc) catch () ) if lumaUnregisterMenu != undefined do try (lumaUnregisterMenu()) catch () local ok = false try ( if lumaEnsureMenu != undefined then ( ok = lumaEnsureMenu force:true ) else ( ok = lumaRegisterMenu() ) ) catch ( messageBox ("Menu registration error:\n" + (getCurrentException() as string)) title:"Luma Tools" ok = false ) try (lumaShowToolsDialog()) catch () local ver = "unknown" if lumaSuiteVersion != undefined do ver = lumaSuiteVersion as string if ver == "unknown" do ( try (ver = lumaReadLocalVersion()) catch () ) local msg = "" msg += "Luma Tools " + ver + " installed (online).\n\n" msg += "CDN:\n" + (lumaRemoteBase as string) + "\n\n" msg += "Local cache:\n" + packageRoot + "\n\n" msg += "Startup script:\n" + startupFile + "\n\n" msg += "On each Max launch the tools re-check VERSION and update the cache when the server is newer.\n\n" if ok then ( msg += "Main menu: Luma Tools\n" if lumaUsesCuiMenus != undefined and (lumaUsesCuiMenus()) do ( msg += "(Max 2025+ Cui menu — restart Max if the menu is not visible yet.)\n" ) msg += "(A tool palette was also opened.)\n\n" ) else ( msg += "Menu bar registration had a problem.\n" msg += "Use the tool palette, or in the Listener run:\n" msg += " lumaEnsureMenu force:true\n\n" ) msg += "First step:\n LUMA Capture… (web) → Import Site Package…" messageBox msg title:"Luma Tools" format "%\n" msg true ) lumaInstallOnlineSuite()