From da7de7b08aa1b35fb306d2f95aa147cbe527487c Mon Sep 17 00:00:00 2001 From: ocbwoy3 Date: Thu, 8 Jan 2026 22:49:48 +0200 Subject: [PATCH] core: loading screen, realism --- .gitmodules | 3 + default.project.json | 26 ++++- .../ClientBootstrap.client.lua | 13 +++ src/ReplicatedStorage/Client/Bootstrap.lua | 83 ++++++++++++++ .../Client/LoadingScreen/App.lua | 70 ++++++++++++ .../LoadingScreen/Components/ProgressBar.lua | 101 ++++++++++++++++++ .../Client/LoadingScreen/init.lua | 55 ++++++++++ src/ReplicatedStorage/Shared/ModLoader.lua | 12 ++- .../Crosshair/LocalScript.client.lua | 6 +- src/StarterGui/Game_UI/LocalScript.client.lua | 8 +- src/StarterGui/Hotbar/LocalScript.client.lua | 4 +- .../Actor/ActorInit.client.lua | 14 +-- .../Actor/BlockInteraction.client.lua | 5 + .../CmdrClient.client.lua | 4 + 14 files changed, 386 insertions(+), 18 deletions(-) create mode 100644 .gitmodules create mode 100644 src/ReplicatedFirst/ClientBootstrap.client.lua create mode 100644 src/ReplicatedStorage/Client/Bootstrap.lua create mode 100644 src/ReplicatedStorage/Client/LoadingScreen/App.lua create mode 100644 src/ReplicatedStorage/Client/LoadingScreen/Components/ProgressBar.lua create mode 100644 src/ReplicatedStorage/Client/LoadingScreen/init.lua diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..69313dc --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "ThirdParty/Character-Realism"] + path = ThirdParty/Character-Realism + url = https://github.com/MaximumADHD/Character-Realism diff --git a/default.project.json b/default.project.json index 5e28f47..8373f92 100644 --- a/default.project.json +++ b/default.project.json @@ -2,43 +2,59 @@ "name": "minecraft-roblox", "tree": { "$className": "DataModel", + "ReplicatedStorage": { "$className": "ReplicatedStorage", "$ignoreUnknownInstances": true, "$path": "src/ReplicatedStorage", + "Packages": { "$className": "Folder", "$path": "Packages" } }, + "ReplicatedFirst": { "$className": "ReplicatedFirst", "$ignoreUnknownInstances": true, "$path": "src/ReplicatedFirst" }, + "ServerScriptService": { "$className": "ServerScriptService", "$ignoreUnknownInstances": true, - "$path": "src/ServerScriptService" + "$path": "src/ServerScriptService", + + "RealismServer": { + "$path": "ThirdParty/Character-Realism/Realism.server.lua" + } }, + "StarterGui": { "$className": "StarterGui", "$ignoreUnknownInstances": true, "$path": "src/StarterGui" }, + "StarterPlayer": { "$className": "StarterPlayer", + "$ignoreUnknownInstances": true, + "StarterPlayerScripts": { "$className": "StarterPlayerScripts", "$ignoreUnknownInstances": true, - "$path": "src/StarterPlayer/StarterPlayerScripts" - }, - "$ignoreUnknownInstances": true + "$path": "src/StarterPlayer/StarterPlayerScripts", + + "RealismClient": { + "$path": "ThirdParty/Character-Realism/RealismClient" + } + } }, + "Workspace": { "$className": "Workspace", "$ignoreUnknownInstances": true, "$path": "src/Workspace" } } -} \ No newline at end of file +} diff --git a/src/ReplicatedFirst/ClientBootstrap.client.lua b/src/ReplicatedFirst/ClientBootstrap.client.lua new file mode 100644 index 0000000..d8ca11c --- /dev/null +++ b/src/ReplicatedFirst/ClientBootstrap.client.lua @@ -0,0 +1,13 @@ +--!native +--!optimize 2 + +local ReplicatedStorage = game:GetService("ReplicatedStorage") + +if not game:IsLoaded() then + game.Loaded:Wait() +end + +local Bootstrap = require(ReplicatedStorage:WaitForChild("Client"):WaitForChild("Bootstrap")) +local objects = ReplicatedStorage:WaitForChild("Objects", 9e9) +objects:WaitForChild("MLLoaded", 9e9) +Bootstrap.start() diff --git a/src/ReplicatedStorage/Client/Bootstrap.lua b/src/ReplicatedStorage/Client/Bootstrap.lua new file mode 100644 index 0000000..56f1a8f --- /dev/null +++ b/src/ReplicatedStorage/Client/Bootstrap.lua @@ -0,0 +1,83 @@ +--!native +--!optimize 2 + +local Players = game:GetService("Players") +local ReplicatedStorage = game:GetService("ReplicatedStorage") + +local LoadingScreen = require(ReplicatedStorage.Client.LoadingScreen) +local ModLoader = require(ReplicatedStorage.Shared.ModLoader) +local ChunkManager = require(ReplicatedStorage.Shared.ChunkManager) +local PlacementManager = require(ReplicatedStorage.Shared.PlacementManager) + +local Bootstrap = {} + +local started = false + +local function ensureFlag(name: string) + local objects = ReplicatedStorage:WaitForChild("Objects") + local existing = objects:FindFirstChild(name) + if existing and existing:IsA("BoolValue") then + return existing + end + local ready = Instance.new("BoolValue") + ready.Name = name + ready.Value = true + ready.Parent = objects + return ready +end + +local contentProvider = game:GetService("ContentProvider") + +local function waitForGameLoaded() + if game:IsLoaded() then + return + end + game.Loaded:Wait() +end + +function Bootstrap.start() + if started then + return + end + started = true + + contentProvider:PreloadAsync(game:GetDescendants()) + + ensureFlag("ClientReady") + local screen = LoadingScreen.mount() + + screen.setStatus("Connecting...") + screen.setDetail("Waiting for server") + screen.setProgress(0.2) + + task.wait(1) + + waitForGameLoaded() + + local modsFolder = ReplicatedStorage:WaitForChild("Mods") + local totalMods = #modsFolder:GetChildren() + local modProgressWeight = 0.6 + + ModLoader.loadModsC(function(index, total, modInstance, success) + total = total > 0 and total or math.max(totalMods, 1) + local ratio = index / total + screen.setStatus(`Modloading progress: {index}/{total}`) + screen.setDetail(`Loading {modInstance.Name}`) + screen.setProgress(0.05 + ratio * modProgressWeight) + if not success then + screen.setDetail(`Failed loading {modInstance.Name}; continuing`) + end + end) + + ensureFlag("CSMLLoaded") -- needed + + screen.setStatus("Joining world...") + screen.setDetail("Syncing with server") + screen.setProgress(0.7) + + task.wait(0.5) + + screen.close() +end + +return Bootstrap diff --git a/src/ReplicatedStorage/Client/LoadingScreen/App.lua b/src/ReplicatedStorage/Client/LoadingScreen/App.lua new file mode 100644 index 0000000..feddf6f --- /dev/null +++ b/src/ReplicatedStorage/Client/LoadingScreen/App.lua @@ -0,0 +1,70 @@ +--!native +--!optimize 2 + +local ReplicatedStorage = game:GetService("ReplicatedStorage") +local Roact = require(ReplicatedStorage.Packages.roact) + +local ProgressBar = require(script.Parent.Components.ProgressBar) + +local theme = { + background = Color3.fromRGB(17, 17, 27), + text = Color3.fromRGB(255, 255, 255), + subtext = Color3.fromRGB(205, 214, 244), +} + +local function LoadingScreen(props) + local status = props.status or "Loading game..." + local detail = props.detail or "Preloading..." + local progress = props.progress or 0 + local visible = props.visible + + return Roact.createElement("ScreenGui", { + Name = "LoadingScreen", + DisplayOrder = 9999, + IgnoreGuiInset = true, + ResetOnSpawn = false, + ZIndexBehavior = Enum.ZIndexBehavior.Sibling, + Enabled = visible ~= false, + }, { + Root = Roact.createElement("Frame", { + Size = UDim2.fromScale(1, 1), + BackgroundColor3 = theme.background, + BorderSizePixel = 0, + }, { + Title = Roact.createElement("TextLabel", { + AnchorPoint = Vector2.new(0.5, 0.5), + Position = UDim2.new(0.5, 0, 0.5, 0), + BackgroundTransparency = 1, + Font = Enum.Font.Code, + Text = status, + TextColor3 = theme.text, + TextSize = 32, + TextWrapped = true, + AutomaticSize = Enum.AutomaticSize.XY, + TextXAlignment = Enum.TextXAlignment.Center, + TextYAlignment = Enum.TextYAlignment.Center, + }, { + Gradient = Roact.createElement("UIGradient", { + Color = ColorSequence.new({ + ColorSequenceKeypoint.new(0, Color3.fromRGB(245, 194, 231)), + ColorSequenceKeypoint.new(0.5, Color3.fromRGB(203, 166, 247)), + ColorSequenceKeypoint.new(1, Color3.fromRGB(137, 180, 250)), + }), + Rotation = 30, + Transparency = NumberSequence.new({ + NumberSequenceKeypoint.new(0, 0), + NumberSequenceKeypoint.new(1, 0), + }), + }), + }), + Progress = Roact.createElement(ProgressBar, { + AnchorPoint = Vector2.new(0.5, 1), + Position = UDim2.new(0.5, 0, 1, -32), + progress = progress, + detail = detail, + }), + }), + }) +end + +return LoadingScreen diff --git a/src/ReplicatedStorage/Client/LoadingScreen/Components/ProgressBar.lua b/src/ReplicatedStorage/Client/LoadingScreen/Components/ProgressBar.lua new file mode 100644 index 0000000..a0d0333 --- /dev/null +++ b/src/ReplicatedStorage/Client/LoadingScreen/Components/ProgressBar.lua @@ -0,0 +1,101 @@ +--!native +--!optimize 2 + +local ReplicatedStorage = game:GetService("ReplicatedStorage") +local Roact = require(ReplicatedStorage.Packages.roact) + +local theme = { + background = Color3.fromRGB(30, 30, 46), + text = Color3.fromHex("#cdd6f4"), + holder = Color3.fromRGB(17, 17, 27), + fill = Color3.fromRGB(255, 255, 255), + stroke = Color3.fromRGB(49, 50, 68), + holderStroke = Color3.fromRGB(108, 112, 134), +} + +local function ProgressBar(props) + local function progressToTransparency(v: number) + local p = math.clamp(v or 0, 0, 1) + return NumberSequence.new({ + NumberSequenceKeypoint.new(0, 0), + NumberSequenceKeypoint.new(p, 0), + NumberSequenceKeypoint.new(p, 1), + NumberSequenceKeypoint.new(1, 1), + }) + end + + local gradientTransparency: NumberSequence + if typeof(props.progress) == "table" and props.progress.map then + gradientTransparency = props.progress:map(progressToTransparency) + else + gradientTransparency = progressToTransparency(props.progress or 0) + end + + return Roact.createElement("Frame", { + Name = "ProgressBar", + AnchorPoint = props.AnchorPoint or Vector2.new(0.5, 1), + Position = props.Position or UDim2.new(0.5, 0, 1, -32), + Size = props.Size or UDim2.new(0, 500, 0, 32), + BackgroundColor3 = theme.background, + BorderSizePixel = 0, + }, { + Label = Roact.createElement("TextLabel", { + AnchorPoint = Vector2.new(0, 0), + Position = UDim2.new(0, 16, 0, -32), + Size = UDim2.new(1, -32, 0, 18), + BackgroundTransparency = 1, + Font = Enum.Font.Code, + Text = props.detail, + TextColor3 = theme.text, + TextSize = 18, + TextXAlignment = Enum.TextXAlignment.Left, + TextYAlignment = Enum.TextYAlignment.Top, + }), + Corner = Roact.createElement("UICorner", { + CornerRadius = UDim.new(0, 16), + }), + Stroke = Roact.createElement("UIStroke", { + Color = theme.stroke, + ApplyStrokeMode = Enum.ApplyStrokeMode.Border, + Thickness = 1, + }), + FillHolder = Roact.createElement("Frame", { + Name = "FillHolder", + AnchorPoint = Vector2.new(0, 1), + Position = UDim2.new(0, 8, 1, -8), + Size = UDim2.new(1, -16, 0, 16), + BackgroundColor3 = theme.holder, + BorderSizePixel = 0, + }, { + Corner = Roact.createElement("UICorner", { + CornerRadius = UDim.new(0, 8), + }), + Stroke = Roact.createElement("UIStroke", { + Color = theme.holderStroke, + ApplyStrokeMode = Enum.ApplyStrokeMode.Border, + Thickness = 1, + }), + Fill = Roact.createElement("Frame", { + AnchorPoint = Vector2.new(0, 0), + BackgroundColor3 = theme.fill, + BorderSizePixel = 0, + Size = UDim2.new(1, 0, 1, 0), + }, { + Corner = Roact.createElement("UICorner", { + CornerRadius = UDim.new(0, 8), + }), + Gradient = Roact.createElement("UIGradient", { + Color = ColorSequence.new({ + ColorSequenceKeypoint.new(0, Color3.fromHex("#f5c2e7")), + ColorSequenceKeypoint.new(0.5, Color3.fromHex("#cba6f7")), + ColorSequenceKeypoint.new(1, Color3.fromHex("#89b4fa")), + }), + Transparency = gradientTransparency, + Rotation = 0, + }), + }), + }), + }) +end + +return ProgressBar diff --git a/src/ReplicatedStorage/Client/LoadingScreen/init.lua b/src/ReplicatedStorage/Client/LoadingScreen/init.lua new file mode 100644 index 0000000..66b907e --- /dev/null +++ b/src/ReplicatedStorage/Client/LoadingScreen/init.lua @@ -0,0 +1,55 @@ +--!native +--!optimize 2 + +local ReplicatedStorage = game:GetService("ReplicatedStorage") +local Roact = require(ReplicatedStorage.Packages.roact) +local Players = game:GetService("Players") + +local App = require(script.App) + +local LoadingScreen = {} + +function LoadingScreen.mount(target: Instance?) + local playerGui = target or Players.LocalPlayer:WaitForChild("PlayerGui") + local statusBinding, setStatus = Roact.createBinding("Loading...") + local detailBinding, setDetail = Roact.createBinding("Preparing client") + local progressBinding, setProgress = Roact.createBinding(0) + local visibleBinding, setVisible = Roact.createBinding(true) + + local handle = Roact.mount(Roact.createElement(App, { + status = statusBinding, + detail = detailBinding, + progress = progressBinding, + visible = visibleBinding, + }), playerGui, "RoactLoadingScreen") + + local closed = false + local function close() + if closed then + return + end + closed = true + setVisible(false) + Roact.unmount(handle) + handle = nil + end + + return { + setStatus = function(text: string) + setStatus(text) + end, + setDetail = function(text: string) + setDetail(text) + end, + setProgress = function(value: number) + setProgress(math.clamp(value or 0, 0, 1)) + end, + show = function() + setVisible(true) + end, + hide = close, + close = close, + } +end + +return LoadingScreen diff --git a/src/ReplicatedStorage/Shared/ModLoader.lua b/src/ReplicatedStorage/Shared/ModLoader.lua index b9b543a..b3471aa 100644 --- a/src/ReplicatedStorage/Shared/ModLoader.lua +++ b/src/ReplicatedStorage/Shared/ModLoader.lua @@ -31,10 +31,13 @@ function ML.loadModsS() end end -function ML.loadModsC() +function ML.loadModsC(onProgress: ((number, number, Instance, boolean) -> ())?) print("[CSModLoader] Loading Mods") - for _, m in pairs(ModsFolder:GetChildren()) do + local mods = ModsFolder:GetChildren() + local total = #mods + + for i, m in ipairs(mods) do local success, reason = pcall(function() -- ignore type err local mod: modContext = require(m) @@ -44,6 +47,11 @@ function ML.loadModsC() if not success then warn(`[CSModLoader] Error loading {m.Name}: {reason}`) end + if onProgress then + pcall(function() + onProgress(i, total, m, success) + end) + end end end diff --git a/src/StarterGui/Crosshair/LocalScript.client.lua b/src/StarterGui/Crosshair/LocalScript.client.lua index 76e9406..aae5681 100644 --- a/src/StarterGui/Crosshair/LocalScript.client.lua +++ b/src/StarterGui/Crosshair/LocalScript.client.lua @@ -10,7 +10,9 @@ local UIS = game:GetService("UserInputService") local TXTS = game:GetService("TextChatService") local TXTS_CIF = TXTS:FindFirstChildOfClass("ChatInputBarConfiguration") -ReplicatedStorage:WaitForChild("Objects"):WaitForChild("MLLoaded") +local objects = ReplicatedStorage:WaitForChild("Objects", 9e9) +objects:WaitForChild("MLLoaded", 9e9) +objects:WaitForChild("CSMLLoaded", 9e9) game:GetService("Players").LocalPlayer.CameraMode = Enum.CameraMode.LockFirstPerson UIS.MouseIconEnabled = false @@ -22,4 +24,4 @@ UIS.InputEnded:Connect(function(k) script.Parent.CrosshairLabel.Visible = not v script.Parent.DummyButton.Modal = v end -end) \ No newline at end of file +end) diff --git a/src/StarterGui/Game_UI/LocalScript.client.lua b/src/StarterGui/Game_UI/LocalScript.client.lua index e5c50e7..0b326bc 100644 --- a/src/StarterGui/Game_UI/LocalScript.client.lua +++ b/src/StarterGui/Game_UI/LocalScript.client.lua @@ -10,7 +10,13 @@ local ui = script.Parent local ReplicatedStorage = game:GetService("ReplicatedStorage") local PlacementState = require(ReplicatedStorage.Shared.PlacementState) -ReplicatedStorage:WaitForChild("Objects"):WaitForChild("MLLoaded") +local objects = ReplicatedStorage:WaitForChild("Objects", 9e9) +objects:WaitForChild("MLLoaded", 9e9) +objects:WaitForChild("CSMLLoaded", 9e9) +local clientReady = ReplicatedStorage.Objects:WaitForChild("ClientReady", 5) +if clientReady and not clientReady.Value then + clientReady:GetPropertyChangedSignal("Value"):Wait() +end local cd = ReplicatedStorage.Objects.ChunkDebug:Clone() local sky = ReplicatedStorage.Objects.Sky:Clone() diff --git a/src/StarterGui/Hotbar/LocalScript.client.lua b/src/StarterGui/Hotbar/LocalScript.client.lua index 9ca52ed..756c6d1 100644 --- a/src/StarterGui/Hotbar/LocalScript.client.lua +++ b/src/StarterGui/Hotbar/LocalScript.client.lua @@ -9,7 +9,9 @@ local ReplicatedStorage = game:GetService("ReplicatedStorage") local UIS = game:GetService("UserInputService") local TextChatService = game:GetService("TextChatService") -ReplicatedStorage:WaitForChild("Objects"):WaitForChild("MLLoaded") +local objects = ReplicatedStorage:WaitForChild("Objects", 9e9) +objects:WaitForChild("MLLoaded", 9e9) +objects:WaitForChild("CSMLLoaded", 9e9) local Roact = require(ReplicatedStorage.Packages.roact) local PM = require(ReplicatedStorage.Shared.PlacementManager) diff --git a/src/StarterPlayer/StarterPlayerScripts/Actor/ActorInit.client.lua b/src/StarterPlayer/StarterPlayerScripts/Actor/ActorInit.client.lua index cca224a..e2505ee 100644 --- a/src/StarterPlayer/StarterPlayerScripts/Actor/ActorInit.client.lua +++ b/src/StarterPlayer/StarterPlayerScripts/Actor/ActorInit.client.lua @@ -7,16 +7,16 @@ end pcall(function() task.synchronize() - game:GetService("Workspace"):WaitForChild("$blockscraft_server",5):Destroy() + task.defer(function() + game:GetService("Workspace"):WaitForChild("$blockscraft_server",9e9):Destroy() + end) end) local ReplicatedStorage = game:GetService("ReplicatedStorage") -ReplicatedStorage:WaitForChild("Objects"):WaitForChild("MLLoaded") - -local ML = require(ReplicatedStorage:WaitForChild("Shared"):WaitForChild("ModLoader")) - -ML.loadModsC() +local objects = ReplicatedStorage:WaitForChild("Objects", 9e9) +objects:WaitForChild("MLLoaded", 9e9) +objects:WaitForChild("CSMLLoaded", 9e9) do local PM = require(ReplicatedStorage:WaitForChild("Shared"):WaitForChild("PlacementManager")) @@ -26,4 +26,4 @@ end do local CM = require(ReplicatedStorage:WaitForChild("Shared"):WaitForChild("ChunkManager")) CM:Init() -end +end \ No newline at end of file diff --git a/src/StarterPlayer/StarterPlayerScripts/Actor/BlockInteraction.client.lua b/src/StarterPlayer/StarterPlayerScripts/Actor/BlockInteraction.client.lua index b6ea2ce..5c20e77 100644 --- a/src/StarterPlayer/StarterPlayerScripts/Actor/BlockInteraction.client.lua +++ b/src/StarterPlayer/StarterPlayerScripts/Actor/BlockInteraction.client.lua @@ -1,4 +1,9 @@ --!native --!optimize 2 +local ReplicatedStorage = game:GetService("ReplicatedStorage") +local objects = ReplicatedStorage:WaitForChild("Objects", 9e9) +objects:WaitForChild("MLLoaded", 9e9) +objects:WaitForChild("CSMLLoaded", 9e9) + return diff --git a/src/StarterPlayer/StarterPlayerScripts/CmdrClient.client.lua b/src/StarterPlayer/StarterPlayerScripts/CmdrClient.client.lua index 66e6da3..135e38c 100644 --- a/src/StarterPlayer/StarterPlayerScripts/CmdrClient.client.lua +++ b/src/StarterPlayer/StarterPlayerScripts/CmdrClient.client.lua @@ -4,5 +4,9 @@ until game:IsLoaded() == true local ReplicatedStorage = game:GetService("ReplicatedStorage") +local objects = ReplicatedStorage:WaitForChild("Objects", 9e9) +objects:WaitForChild("MLLoaded", 9e9) +objects:WaitForChild("CSMLLoaded", 9e9) + local Cmdr = require(ReplicatedStorage:WaitForChild("CmdrClient")) Cmdr:SetActivationKeys({ Enum.KeyCode.F2 })