Skip to content

Commit

Permalink
Merge branch 'master' into loop-param-names
Browse files Browse the repository at this point in the history
  • Loading branch information
jackTabsCode authored Apr 23, 2024
2 parents 3dfffbc + 9c0fa7e commit c6ad03b
Show file tree
Hide file tree
Showing 24 changed files with 573 additions and 177 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ on:
tags: ["v*"]

jobs:
release:
build:
name: Release
runs-on: ubuntu-latest
steps:
Expand Down Expand Up @@ -38,4 +38,4 @@ jobs:
- name: Publish
run: npm publish
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }}
NODE_AUTH_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }}
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,23 @@ The format is based on [Keep a Changelog][kac], and this project adheres to

## [Unreleased]


## [0.8.0] - 2024-04-22

### Changed
- Added `Views` for random-accessing entities within queries.
- Views are optimized for terse indexing, making them useful for traversing graphs of entities.
- Added `Debugger.loopParameterNames` which allows for labelling things passed to Loop.
- Disabled `AutoLocalize` on many Plasma Widgets.
- This removes unnecessary computations for `LocalizationService::attemptLocalization`.
- Improved `QueryResult:without` to narrow archetype invariants.
- The filter now works on the archetype-level rather than filtering entities
ad-hoc which will immensely improve query performance.

### Fixed

- Fixed the Scheduler not respecting priorties of systems.
- Fixed padding of items in the Debugger's state view.
## [0.7.1] - 2024-01-31

### Changed
Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
<a href="https://matter-ecs.github.io/matter/">
<img src="https://github.com/matter-ecs/matter/actions/workflows/docs.yaml/badge.svg" alt="Docs status">
</a>
<a href="https://discord.gg/6cvzthZC4X">
<img src="https://dcbadge.vercel.app/api/server/6cvzthZC4X?style=flat" alt="OSS Discord">
</a>
</div>
<br>

Expand Down
2 changes: 1 addition & 1 deletion default.project.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "matter",
"tree": {
"$path": "lib"
"$path": "out"
}
}
4 changes: 1 addition & 3 deletions docs/BestPractices/Reconciliation.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ Reconciliation, in this context, means taking state from one form and turning it

When writing code in an ECS like Matter, it's ideal for all of our gameplay code to operate on the ECS world alone. In the Matter example game, for example, there are ships that fly to certain points in space. For example, instead of updating the ships in the Data Model directly, we store the current goal position in the Ship component. The Ship component knows nothing about the Data Model. It has no reference to the physical ship Instance in the Data Model, it only contains the state about the ship.

We can create another component (in the [Matter example game](https://github.com/evaera/matter/tree/main/example), we call it `Model`) that holds a reference to the ship Instance.
We can create another component (in the [Matter example game](https://github.com/matter-ecs/matter/tree/main/example), we call it `Model`) that holds a reference to the ship Instance.

We can loop over all Ships that don't also have a Model, and create one for it.

Expand Down Expand Up @@ -116,7 +116,6 @@ We can make a system that handles both of these cases for us.
for id, transformRecord in world:queryChanged(Transform) do

local model = world:get(id, Model)

-- Take care to ignore the changed event if it was us that triggered it
if model and transformRecord.new and not transformRecord.new.doNotReconcile then
model.instance:SetPrimaryPartCFrame(transformRecord.new.cframe)
Expand All @@ -127,7 +126,6 @@ end
for id, modelRecord in world:queryChanged(Model) do

local transform = world:get(id, Transform)

if transform and modelRecord.new then
modelRecord.new.model:SetPrimaryPartCFrame(transform.cframe)
end
Expand Down
2 changes: 1 addition & 1 deletion docs/GettingStarted.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,4 +91,4 @@ value, so we don't have to mutate the existing component.
## Next steps
You should dive in to the [API reference](/api/Matter)! The Matter API is simple and documented in detail.

And if you haven't already, check out the [`/example` directory in the matter repo](https://github.com/evaera/matter/tree/main/example/).
And if you haven't already, check out the [`/example` directory in the matter repo](https://github.com/matter-ecs/matter/tree/main/example/).
8 changes: 6 additions & 2 deletions docs/Guides/CollectionService.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ sidebar_position: 4

# Using CollectionService tags

As a pure ECS first and foremost, Matter provides no special functionality for CollectionService tags out of the box. However, it's rather simple to implement this yourself. Here's an example taken from the official [Matter example game](https://github.com/evaera/matter/tree/main/example/server).
As a pure ECS first and foremost, Matter provides no special functionality for CollectionService tags out of the box. However, it's rather simple to implement this yourself. Here's an example taken from the official [Matter example game](https://github.com/matter-ecs/matter/tree/main/example/src/server).

```lua
local CollectionService = game:GetService("CollectionService")
Expand Down Expand Up @@ -52,4 +52,8 @@ return setupTags

```

This example can be modified to meet your game's needs as you see fit.
<<<<<<< HEAD
This example can be modified to meet your game's needs as you see fit.
=======
This example can be modified to meet your game's needs as you see fit.
>>>>>>> 627635d72d2278846eacb4a446ee4a2e85817fa1
2 changes: 1 addition & 1 deletion docs/Guides/HotReloading.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,5 +89,5 @@ end, function(_, context)
end)
```

That's it! For a real example of this in action, check out the [Matter example game](https://github.com/evaera/matter/blob/main/example/src/shared/start.lua).
That's it! For a real example of this in action, check out the [Matter example game](https://github.com/matter-ecs/matter/blob/main/example/src/shared/start.lua).

12 changes: 7 additions & 5 deletions docs/Guides/MatterDebugger.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,20 @@ local function spinSpinners(world, _, ui)
end
```

This is accomplished using [Plasma](https://eryn.io/plasma/), an immediate-mode widget library. The widgets are only created while the debugger is active. Leaving the widget calls in your systems all the time is fine, because calling a widget function when the debugger is not open is a no-op.
This is accomplished using [Plasma](https://matter-ecs.github.io/plasma/), an immediate-mode widget library. The widgets are only created while the debugger is active. Leaving the widget calls in your systems all the time is fine, because calling a widget function when the debugger is not open is a no-op.

The [Matter example game](https://github.com/evaera/matter/blob/main/example/shared/start.lua) comes with the debugger set up already. If you want to see an example of the debugger already set up in a game, check out that page.
The [Matter example game](https://github.com/matter-ecs/matter/blob/main/example/shared/start.lua) comes with the debugger set up already. If you want to see an example of the debugger already set up in a game, check out that page.

## Adding the Matter debugger to your game

### Installing Plasma
You need to install [Plasma](https://eryn.io/plasma/) as a dependency to your project. We recommend you do this with [Wally](https://wally.run), the Roblox open source package manager.
You need to install [Plasma](https://matter-ecs.github.io/plasma/) as a dependency to your project. We recommend you do this with [Wally](https://wally.run), the Roblox open source package manager.

To find the latest version of Plasma, check out [this page](https://wally.run/package/matter-ecs/plasma) or run `wally search matter-ecs`.

```toml title="wally.toml"
[dependencies]
plasma = "evaera/plasma@0.4.2"
plasma = "matter-ecs/plasma@0.4.2"
```

### Create the debugger
Expand Down Expand Up @@ -145,7 +147,7 @@ The following Plasma widgets are available:
- table
- window

For details on these widgets, check out the [Plasma docs](https://eryn.io/plasma/api/Plasma)
For details on these widgets, check out the [Plasma docs](https://matter-ecs.github.io/plasma/api/Plasma)

## Demo videos

Expand Down
8 changes: 8 additions & 0 deletions docs/Guides/Migration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Migration
## Migrating from evaera/matter to matter-ecs/matter
Migrating from `evaera/matter` to `matter-ecs/matter` is easy! The only thing you need to do is change the package name in your `wally.toml` file.

```toml title="wally.toml"
[dependencies]
matter = "matter-ecs/matter@0.7.0"
```
4 changes: 2 additions & 2 deletions docs/Guides/Replication.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Replication is not built into Matter, but it's easy to implement yourself. This guide will give you an overview of one way to implement replication with Matter.

This article will cover the way the [Matter example game](https://github.com/evaera/matter/blob/main/example/shared/start.lua) implements replication.
This article will cover the way the [Matter example game](https://github.com/matter-ecs/matter/blob/main/example/shared/start.lua) implements replication.

## Deciding which components to replicate

Expand Down Expand Up @@ -35,7 +35,7 @@ RemoteEvent.Name = "MatterRemote"
RemoteEvent.Parent = ReplicatedStorage
```

Let's convert the list of component names into actual components. This is assuming you have a Components module that exports your components, like [the matter example game does](https://github.com/evaera/matter/blob/main/example/shared/components.lua).
Let's convert the list of component names into actual components. This is assuming you have a Components module that exports your components, like [the matter example game does](https://github.com/matter-ecs/matter/blob/main/example/shared/components.lua).

```lua
local replicatedComponents = {}
Expand Down
2 changes: 1 addition & 1 deletion docs/Installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ registry = "https://github.com/UpliftGames/wally-index"
realm = "shared"

[dependencies]
matter = "evaera/matter@X.X.X" # Don't copy this. This won't work.
matter = "matter-ecs/matter@X.X.X" # Don't copy this. This won't work.
# Copy real string from page linked above.
```

Expand Down
82 changes: 39 additions & 43 deletions lib/Loop.lua
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,6 @@ local function systemName(system: System)
return debug.info(fn, "s") .. "->" .. debug.info(fn, "n")
end

local function systemPriority(system: System)
if type(system) == "table" then
return system.priority or 0
end

return 0
end

--[=[
@class Loop
Expand Down Expand Up @@ -222,9 +214,46 @@ function Loop:replaceSystem(old: System, new: System)
end

local function orderSystemsByDependencies(unscheduledSystems: { System })
local sortedUnscheduledSystems = table.clone(unscheduledSystems)
local systemPriorityMap = {}
local visiting = "v"

local function systemPriority(system: System)
local priority = systemPriorityMap[system]

if not priority then
priority = 0

systemPriorityMap[system] = visiting

if type(system) == "table" then
if system.after then
for _, dependency in system.after do
if systemPriorityMap[dependency] ~= visiting then
priority = math.max(priority, systemPriority(dependency) + 1)
else
local errorStatement = {
`Cyclic dependency detected: System '{systemName(system)}' is set to execute after System '{systemName(
dependency
)}', and vice versa. This creates a loop that prevents the systems from being able to execute in a valid order.`,
"To resolve this issue, reconsider the dependencies between these systems. One possible solution is to update the 'after' field from one of the systems.",
}
error(table.concat(errorStatement, "\n"))
end
end
elseif system.priority then
priority = system.priority
end
end

systemPriorityMap[system] = priority
end

table.sort(sortedUnscheduledSystems, function(a, b)
return priority
end

local scheduledSystems = table.clone(unscheduledSystems)

table.sort(scheduledSystems, function(a, b)
local priorityA = systemPriority(a)
local priorityB = systemPriority(b)

Expand All @@ -242,39 +271,6 @@ local function orderSystemsByDependencies(unscheduledSystems: { System })
return priorityA < priorityB
end)

local scheduledSystemsSet = {}
local scheduledSystems = {}

local visited, explored = 1, 2

local function scheduleSystem(system)
scheduledSystemsSet[system] = visited

if type(system) == "table" and system.after then
for _, dependency in system.after do
if scheduledSystemsSet[dependency] == nil then
scheduleSystem(dependency)
elseif scheduledSystemsSet[dependency] == visited then
error(
`Unable to schedule systems due to cyclic dependency between: \n{systemName(system)} \nAND \n{systemName(
dependency
)}`
)
end
end
end

scheduledSystemsSet[system] = explored

table.insert(scheduledSystems, system)
end

for _, system in sortedUnscheduledSystems do
if scheduledSystemsSet[system] == nil then
scheduleSystem(system)
end
end

return scheduledSystems
end

Expand Down
42 changes: 42 additions & 0 deletions lib/Loop.spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,48 @@ return function()
connection.default:Disconnect()
end)

it("should call systems in order with dependent system after priority system", function()
local loop = Loop.new()

local order = {}
local systemB = {
system = function()
table.insert(order, "b")
end,
}
local systemC = {
system = function()
table.insert(order, "c")
end,
priority = 1000,
}
local systemA = {
system = function()
table.insert(order, "a")
end,
after = { systemC },
}

loop:scheduleSystems({
systemB,
systemC,
systemA,
})

local connection = loop:begin({ default = bindable.Event })

expect(#order).to.equal(0)

bindable:Fire()

expect(#order).to.equal(3)
expect(order[1]).to.equal("b")
expect(order[2]).to.equal("c")
expect(order[3]).to.equal("a")

connection.default:Disconnect()
end)

it("should not schedule systems more than once", function()
local loop = Loop.new()

Expand Down
Loading

0 comments on commit c6ad03b

Please sign in to comment.