From 0.8.2 Stable
Maelstrom-2 is a canary build. It intentionally includes breaking changes that improve type quality, plugin extensibility, network safety, and runtime determinism.
Use this page when migrating from the 0.8.2 stable release.
Package Version
Section titled âPackage VersionâUpdate the package version:
[dependencies]Riptide = "riptide/core@0.9.0-maelstrom.2"Side-Specific API
Section titled âSide-Specific APIâEntry scripts should import the side they launch. Modules then receive that same side-specific API in Init, Start, and player hooks.
-- Before, inside a server serviceRiptide.Network.Register("Ping", callback)local service = Riptide.GetService("DataService")Riptide.State:SetForPlayer(player, "coins", 100)
-- After, inside a server serviceRiptide.Network.Register("Ping", callback)local service = Riptide.GetService("DataService")Riptide.State:SetForPlayer(player, "coins", 100)The difference is the value passed to the module: server modules receive Riptide.Server, and client modules receive Riptide.Client.
-- ServerScriptService/main.server.lualocal Riptide = require(ReplicatedStorage.Packages.Riptide).Server
Riptide.Launch({ ModulesFolder = ServerScriptService.Services,})Network Request/Response
Section titled âNetwork Request/ResponseâIf your 0.8.x code used InvokeServer, InvokeClient, or RemoteFunction-style APIs, migrate to explicit request/response events. Maelstrom networking is RemoteEvent-based to avoid blocking server threads on clients.
-- ServerRiptide.Network.RegisterTyped("RequestCoins", {}, function(player) local coins = Riptide.State:Get("coins", player) or 0 Riptide.Network.FireClient(player, "CoinsResponse", coins)end)
-- ClientRiptide.Network.Register("CoinsResponse", function(coins) print("Coins:", coins)end)
Riptide.Network.FireServer("RequestCoins")Network Middleware
Section titled âNetwork MiddlewareâMiddleware now receives the payload as a mutable args table. Return false to stop dispatch.
-- Server middlewareRiptide.Network.UseMiddleware(function(player, eventName, args) if eventName == "BuyItem" and typeof(args[2]) ~= "number" then return false end
return trueend)Typed Network
Section titled âTyped NetworkâKeep RegisterTyped for boundary validation. Add typed proxies when you want field autocomplete for known events.
local Network = Riptide.Network.TypedServer<NetworkEvents>()Network.PurchaseResult:FireClient(player, true, "ok")State Replication
Section titled âState ReplicationâState remains server-authoritative, but Maelstrom-2 hardens malformed payload handling and adds typed key proxies.
local State = Riptide.State.TypedServer<StateSchema>()State.coins:SetForPlayer(player, 100)On the client:
local State = Riptide.State.TypedClient<StateSchema>()State.coins:Subscribe(function(coins) print(coins or 0)end)Async.Parallel
Section titled âAsync.ParallelâAsync.Parallel now returns explicit result objects, so successful nil, thrown errors, and timeouts are distinguishable.
local results = Riptide.Async.Parallel({ function() return "loaded" end, function() return nil end,}, 5)
for _, result in ipairs(results) do if result.ok then print(result.value) elseif result.timedOut then warn("Timed out") else warn(result.error) endendPlugins
Section titled âPluginsâPlugins are new framework-layer extensions. They load and initialize before game modules, then reach Start readiness before player replay and module Start.
Riptide.Launch({ PluginsFolder = ServerScriptService.Plugins, ModulesFolder = ServerScriptService.Services,})Normal plugin sandboxes do not expose raw core event access such as OnCoreEvent. Use namespaced sandbox APIs like sandbox:OnNetworkEvent, sandbox:Emit, sandbox:On, sandbox:Once, and sandbox:GetPluginAPI.
Lifecycle Order
Section titled âLifecycle OrderâThe server startup order is now:
1. Riptide bootstrap2. Plugin Load + Init3. Shared and server module Load4. ComponentService start5. Module Init6. Plugin Start readiness7. PlayerLifecycle replay8. Module StartMove code that must exist before services/controllers into plugin Init. Move bounded setup that must complete before gameplay into plugin Start.
Luau Directives
Section titled âLuau DirectivesâMaelstrom-2 expects --!strict as the first line in source and test files. The test suite includes a directive policy check for this.
Validation Checklist
Section titled âValidation Checklistâ- Import
.Server/.Clientin entry scripts and use the injected side API in modules. - Replace any RemoteFunction request/response flow with event pairs.
- Update Network middleware signatures to args-table form.
- Update
Async.Parallelcallers to inspect result objects. - Move framework extensions into plugins when they should be available before game modules start.
- Run Lune tests and Studio QA after migration.