Skip to content

Commit 9b79edd

Browse files
authored
release: 2025.2.2
2 parents 325b15c + df7a9bd commit 9b79edd

File tree

12 files changed

+110
-29
lines changed

12 files changed

+110
-29
lines changed

.circleci/config.yml

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -95,20 +95,16 @@ jobs:
9595
command: mkdir C:\Users\circleci\dist
9696

9797
- run:
98-
name: Upgrade pip
99-
command: python -m pip install --upgrade pip
98+
name: Install uv
99+
command: python -m pip install uv
100100

101101
- run:
102102
name: Install python dependencies
103-
command: python -m pip install -r requirements.txt
103+
command: uv sync
104104

105-
- run: python -m pip install wheel
105+
- run: uv add ordered-set nuitka
106106

107-
- run: python -m pip install ordered-set
108-
109-
- run: python -m pip install nuitka
110-
111-
- run: python -m nuitka --standalone --assume-yes-for-downloads --include-data-files=VERSION=VERSION --include-data-files=server_config.cfg=server_config.cfg --include-data-dir=static=static --include-data-dir=templates=templates --windows-icon-from-ico=static/favicon.ico ./planarally.py
107+
- run: uv run -m nuitka --standalone --assume-yes-for-downloads --include-data-files=VERSION=VERSION --include-data-files=server_config.cfg=server_config.cfg --include-data-dir=static=static --include-data-dir=templates=templates --windows-icon-from-ico=static/favicon.ico ./planarally.py
112108

113109
- run:
114110
name: Zip artifacts

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,14 @@ tech changes will usually be stripped from release notes for the public
1010

1111
## Unreleased
1212

13+
## [2025.2.2]
14+
15+
### Fixed
16+
17+
- Initiative effect rename losing focus after pressing 1 character
18+
- Locked shapes not being selectable directly
19+
- DM only auras where no longer rendered due to a bug in the new access logic
20+
1321
## [2025.2.1]
1422

1523
This fixes/adds support for a wider variety of smtp email servers.

client/package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

client/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "client",
3-
"version": "2025.2.1",
3+
"version": "2025.2.2",
44
"private": true,
55
"type": "module",
66
"scripts": {

client/src/game/systems/access/index.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ class AccessSystem implements ShapeSystem {
5151
clear(): void {
5252
this.dropState();
5353
for (const al of ACCESS_LEVELS) {
54-
$.activeTokenFilters.set(al, undefined);
54+
$.activeTokenFilters.delete(al);
5555
$.ownedTokens.get(al)?.clear();
5656
}
5757
mutable.access.clear();
@@ -112,6 +112,11 @@ class AccessSystem implements ShapeSystem {
112112

113113
const _access: AccessLevel[] = Array.isArray(access) ? access : [access];
114114

115+
// This is an extra case to cover the special case where the shape has no attached users
116+
// in which case the auras would be hidden for the DM with the normal behaviour.
117+
// If we're not actively filtering tokens, we can assume we want to show them.
118+
if (gameState.raw.isDm && _access.every((al) => !raw.activeTokenFilters.has(al))) return true;
119+
115120
// 2. Otherwise check in the active tokens or owned tokens depending on the limitToActiveTokens flag
116121
return _access.every((al) => (limitToActiveTokens ? activeTokens.value : raw.ownedTokens).get(al)?.has(id));
117122
}

client/src/game/systems/access/state.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ interface ReactiveAccessState {
1717
playerAccess: Map<string, AccessConfig>;
1818

1919
ownedTokens: Map<AccessLevel, Set<LocalId>>;
20-
activeTokenFilters: Map<AccessLevel, Set<LocalId> | undefined>;
20+
activeTokenFilters: Map<AccessLevel, Set<LocalId>>;
2121
}
2222

2323
interface AccessState {
@@ -35,7 +35,7 @@ const state = buildState<ReactiveAccessState, AccessState>(
3535
playerAccess: new Map(),
3636

3737
ownedTokens: new Map(ACCESS_LEVELS.map((al) => [al, new Set<LocalId>()])),
38-
activeTokenFilters: new Map(ACCESS_LEVELS.map((al) => [al, undefined])),
38+
activeTokenFilters: new Map(),
3939
},
4040
{ access: new Map<LocalId, AccessMap>() },
4141
);

client/src/game/tools/variants/select/index.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ enum SelectOperations {
7070
Noop,
7171
Resize,
7272
Drag,
73+
Select,
7374
GroupSelect,
7475
Rotate,
7576
}
@@ -227,12 +228,15 @@ class SelectTool extends Tool implements ISelectTool {
227228
}
228229

229230
// Syncs the local state tracked selection state to the global selection state
230-
private syncSelection(): void {
231+
private syncSelection(resetCurrentSelection = false): void {
231232
if (this.currentSelection.length === 0) {
232233
selectedSystem.clear();
233234
} else {
234235
selectedSystem.set(...this.currentSelection.map((s) => s.id));
235236
}
237+
if (resetCurrentSelection) {
238+
this.currentSelection = [];
239+
}
236240
}
237241

238242
// INPUT HANDLERS
@@ -382,6 +386,8 @@ class SelectTool extends Tool implements ISelectTool {
382386
visionState.removeBlocker(TriangulationTarget.MOVEMENT, shape.floorId, shape, true);
383387
}
384388
}
389+
} else {
390+
this.mode = SelectOperations.Select;
385391
}
386392
layer.invalidate(true);
387393
hit = true;
@@ -834,19 +840,19 @@ class SelectTool extends Tool implements ISelectTool {
834840
if (recalcVision) visionState.recalculateVision(floorId);
835841
if (recalcMovement) visionState.recalculateMovement(floorId);
836842
}
837-
layer.invalidate(false);
838843

839844
if (this.mode !== SelectOperations.Rotate) {
840845
this.removeRotationUi();
841846
this.createRotationUi(features);
842847
}
843848
}
844849

850+
layer.invalidate(false);
851+
845852
if (this.operationReady) addOperation(this.operationList!);
846853

847854
_$.hasSelection = this.currentSelection.length > 0;
848-
this.syncSelection();
849-
this.currentSelection = [];
855+
this.syncSelection(true);
850856

851857
this.mode = SelectOperations.Noop;
852858
}

client/src/game/ui/initiative/Initiative.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,7 @@ function n(e: any): number {
268268
class="initiative-effect"
269269
:class="{ 'effect-visible': alwaysShowEffects }"
270270
>
271-
<div v-for="(effect, e) of actor.effects" :key="`${actor.globalId}-${effect.name}`">
271+
<div v-for="(effect, e) of actor.effects" :key="`${actor.globalId}-${e}`">
272272
<input
273273
v-model="effect.name"
274274
type="text"

client/test/game/systems/access/index.test.ts

Lines changed: 72 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -114,18 +114,26 @@ describe("Access System", () => {
114114
let id: LocalId;
115115
let id2: LocalId;
116116
let id3: LocalId;
117+
let id4: LocalId;
118+
let id5: LocalId;
117119

118120
beforeEach(() => {
119121
// Shape 1: Only some default access rights
120122
// Shape 2: Full access rights for a specific user
121123
// Shape 3: Mixed default & specific access rights
124+
// Shape 4: No access rights
125+
// Shape 5: Player with only edit access rights
122126
id = generateTestLocalId();
123127
id2 = generateTestLocalId();
124128
id3 = generateTestLocalId();
129+
id4 = generateTestLocalId();
130+
id5 = generateTestLocalId();
125131

126132
const id1Default = { edit: true, movement: false, vision: true };
127133
const id2Default = { edit: false, movement: true, vision: false };
128134
const id3Default = { edit: false, movement: false, vision: true };
135+
const id4Default = { edit: false, movement: false, vision: false };
136+
const id5Default = { edit: false, movement: false, vision: false };
129137

130138
const id2TestUser = {
131139
access: { edit: true, movement: true, vision: true },
@@ -153,6 +161,14 @@ describe("Access System", () => {
153161
default: id3Default,
154162
extra: [id3TestUser],
155163
});
164+
accessSystem.inform(id4, {
165+
default: id4Default,
166+
extra: [],
167+
});
168+
accessSystem.inform(id5, {
169+
default: id5Default,
170+
extra: [id3TestUser],
171+
});
156172
});
157173
it("should return true for the DM without limiters", () => {
158174
// setup
@@ -161,6 +177,8 @@ describe("Access System", () => {
161177
accessCheck(id, { edit: true, movement: true, vision: true });
162178
accessCheck(id2, { edit: true, movement: true, vision: true });
163179
accessCheck(id3, { edit: true, movement: true, vision: true });
180+
accessCheck(id4, { edit: true, movement: true, vision: true });
181+
accessCheck(id5, { edit: true, movement: true, vision: true });
164182
// teardown
165183
gameSystem.setDm(false);
166184
});
@@ -172,36 +190,72 @@ describe("Access System", () => {
172190
accessCheck(id, { edit: true, movement: true, vision: true });
173191
accessCheck(id2, { edit: true, movement: true, vision: true });
174192
accessCheck(id3, { edit: true, movement: true, vision: true });
193+
accessCheck(id4, { edit: true, movement: true, vision: true });
194+
accessCheck(id5, { edit: true, movement: true, vision: true });
175195
// teardown
176196
gameSystem.setDm(false);
177197
});
178-
it("should return according to the access rights for the DM with a limiter and the shape in the active filter", () => {
198+
it("should return true for the DM with a limiter and the shape in the active filter", () => {
179199
// setup
180200
gameSystem.setDm(true);
181-
// test shape 1
201+
// test only shape 1 active
182202
accessSystem.setActiveVisionTokens(id);
183-
accessCheck(id, { edit: true, movement: false, vision: true }, true);
203+
accessCheck(id, { edit: true, movement: true, vision: true }, true);
184204
// test shape 2
185205
accessSystem.setActiveVisionTokens(id2);
186206
accessCheck(id2, { edit: true, movement: true, vision: true }, true);
187207
// test shape 3
188208
accessSystem.setActiveVisionTokens(id3);
189-
accessCheck(id3, { edit: true, movement: false, vision: true }, true);
209+
accessCheck(id3, { edit: true, movement: true, vision: true }, true);
210+
// test shape 4
211+
accessSystem.setActiveVisionTokens(id4);
212+
accessCheck(id4, { edit: true, movement: true, vision: true }, true);
213+
// test shape 5
214+
accessSystem.setActiveVisionTokens(id5);
215+
accessCheck(id5, { edit: true, movement: true, vision: true }, true);
190216
// test all shapes
191-
accessSystem.setActiveVisionTokens(id, id2, id3);
217+
accessSystem.setActiveVisionTokens(id, id2, id3, id4, id5);
192218
expect(accessSystem.hasAccessTo(id, "edit", true)).toBe(true);
193219
expect(accessSystem.hasAccessTo(id2, "movement", true)).toBe(true);
194220
expect(accessSystem.hasAccessTo(id3, "vision", true)).toBe(true);
221+
expect(accessSystem.hasAccessTo(id4, "vision", true)).toBe(true);
222+
expect(accessSystem.hasAccessTo(id5, "vision", true)).toBe(true);
195223
// teardown
196224
gameSystem.setDm(false);
197225
});
198226
it("should return false for the DM with a limiter and the shape not in the active filter", () => {
199227
// setup
200228
gameSystem.setDm(true);
229+
// test shape 1 active
201230
accessSystem.setActiveVisionTokens(id);
202-
// test
203231
expect(accessSystem.hasAccessTo(id2, "vision", true)).toBe(false);
204232
expect(accessSystem.hasAccessTo(id3, "vision", true)).toBe(false);
233+
expect(accessSystem.hasAccessTo(id4, "vision", true)).toBe(false);
234+
expect(accessSystem.hasAccessTo(id5, "vision", true)).toBe(false);
235+
// test shape 2 active
236+
accessSystem.setActiveVisionTokens(id2);
237+
expect(accessSystem.hasAccessTo(id, "vision", true)).toBe(false);
238+
expect(accessSystem.hasAccessTo(id3, "vision", true)).toBe(false);
239+
expect(accessSystem.hasAccessTo(id4, "vision", true)).toBe(false);
240+
expect(accessSystem.hasAccessTo(id5, "vision", true)).toBe(false);
241+
// test shape 3 active
242+
accessSystem.setActiveVisionTokens(id3);
243+
expect(accessSystem.hasAccessTo(id, "vision", true)).toBe(false);
244+
expect(accessSystem.hasAccessTo(id2, "vision", true)).toBe(false);
245+
expect(accessSystem.hasAccessTo(id4, "vision", true)).toBe(false);
246+
expect(accessSystem.hasAccessTo(id5, "vision", true)).toBe(false);
247+
// test shape 4 active
248+
accessSystem.setActiveVisionTokens(id4);
249+
expect(accessSystem.hasAccessTo(id, "vision", true)).toBe(false);
250+
expect(accessSystem.hasAccessTo(id2, "vision", true)).toBe(false);
251+
expect(accessSystem.hasAccessTo(id3, "vision", true)).toBe(false);
252+
expect(accessSystem.hasAccessTo(id5, "vision", true)).toBe(false);
253+
// test shape 5 active
254+
accessSystem.setActiveVisionTokens(id5);
255+
expect(accessSystem.hasAccessTo(id, "vision", true)).toBe(false);
256+
expect(accessSystem.hasAccessTo(id2, "vision", true)).toBe(false);
257+
expect(accessSystem.hasAccessTo(id3, "vision", true)).toBe(false);
258+
expect(accessSystem.hasAccessTo(id4, "vision", true)).toBe(false);
205259
// teardown
206260
gameSystem.setDm(false);
207261
});
@@ -260,6 +314,10 @@ describe("Access System", () => {
260314
accessCheck(id2, { edit: false, movement: true, vision: false });
261315
// Shape 3
262316
accessCheck(id3, { edit: false, movement: false, vision: true });
317+
// Shape 4
318+
accessCheck(id4, { edit: false, movement: false, vision: false });
319+
// Shape 5
320+
accessCheck(id5, { edit: false, movement: false, vision: false });
263321
// User 2: Full access
264322
coreStore.setUsername("userWithFullRights");
265323
// Shape 1
@@ -268,6 +326,10 @@ describe("Access System", () => {
268326
accessCheck(id2, { edit: true, movement: true, vision: true });
269327
// Shape 3
270328
accessCheck(id3, { edit: false, movement: false, vision: true });
329+
// Shape 4
330+
accessCheck(id4, { edit: false, movement: false, vision: false });
331+
// Shape 5
332+
accessCheck(id5, { edit: false, movement: false, vision: false });
271333
// User 3: Mixed access
272334
coreStore.setUsername("userWithLimitedRights");
273335
// Shape 1
@@ -276,6 +338,10 @@ describe("Access System", () => {
276338
accessCheck(id2, { edit: false, movement: true, vision: false });
277339
// Shape 3
278340
accessCheck(id3, { edit: true, movement: false, vision: true });
341+
// Shape 4
342+
accessCheck(id4, { edit: false, movement: false, vision: false });
343+
// Shape 5
344+
accessCheck(id5, { edit: true, movement: false, vision: false });
279345
});
280346
});
281347
describe("getAccess", () => {

server/VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
2025.2.1
1+
2025.2.2
22

33
**Server owners:** The server config has changed location and format. Ensure you check the release notes for the necessary info.
44
The old config files are not yet removed from git to prevent losing modifications you made to these files.

0 commit comments

Comments
 (0)