diff --git a/app/imports/api/creature/log/CreatureLogs.js b/app/imports/api/creature/log/CreatureLogs.js
index 633d1ff3..dddd2cc5 100644
--- a/app/imports/api/creature/log/CreatureLogs.js
+++ b/app/imports/api/creature/log/CreatureLogs.js
@@ -43,13 +43,10 @@ let CreatureLogSchema = new SimpleSchema({
type: String,
index: 1,
},
- // creatures targeted by any of the logged events
- targetIds: {
- type: Array,
- optional: true,
- },
- 'targetIds.$': {
+ // The tabletops this log is associated with
+ tabletopId: {
type: String,
+ optional: true,
index: 1,
},
creatureName: {
diff --git a/app/imports/api/engine/action/EngineActions.ts b/app/imports/api/engine/action/EngineActions.ts
index 46a7e7c0..cd8160a1 100644
--- a/app/imports/api/engine/action/EngineActions.ts
+++ b/app/imports/api/engine/action/EngineActions.ts
@@ -11,6 +11,7 @@ export interface EngineAction {
_decisions?: any[],
creatureId: string;
rootPropId?: string;
+ tabletopId: string;
targetIds?: string[];
results: TaskResult[];
taskCount: number;
@@ -20,11 +21,18 @@ const ActionSchema = new SimpleSchema({
creatureId: {
type: String,
regEx: SimpleSchema.RegEx.Id,
+ index: 1,
},
rootPropId: {
type: String,
regEx: SimpleSchema.RegEx.Id,
},
+ tabletopId: {
+ type: String,
+ regEx: SimpleSchema.RegEx.Id,
+ optional: true,
+ index: 1,
+ },
targetIds: {
type: Array,
defaultValue: [],
diff --git a/app/imports/api/engine/action/functions/writeActionResults.ts b/app/imports/api/engine/action/functions/writeActionResults.ts
index 5b3f51b6..b8f08309 100644
--- a/app/imports/api/engine/action/functions/writeActionResults.ts
+++ b/app/imports/api/engine/action/functions/writeActionResults.ts
@@ -26,7 +26,7 @@ export default async function writeActionResults(action: EngineAction) {
const logPromise = CreatureLogs.insertAsync({
content: logContents,
creatureId: action.creatureId,
- targetIds: allTargetIds,
+ tabletopId: action.tabletopId,
});
// Write the bulk updates
diff --git a/app/imports/api/engine/action/methods/insertAction.ts b/app/imports/api/engine/action/methods/insertAction.ts
index ebafe622..7b4c0327 100644
--- a/app/imports/api/engine/action/methods/insertAction.ts
+++ b/app/imports/api/engine/action/methods/insertAction.ts
@@ -9,8 +9,29 @@ export const insertAction = new ValidatedMethod({
validate: new SimpleSchema({
action: ActionSchema
}).validator({ clean: true }),
+ rateLimit: {
+ numRequests: 5,
+ timeInterval: 1000,
+ },
run: function ({ action }: { action: EngineAction }) {
- assertEditPermission(getCreature(action.creatureId), this.userId);
+ const creature = getCreature(action.creatureId);
+ assertEditPermission(getCreature(creature), this.userId);
+ // Make sure the action shares the creature's tabletopId
+ // It is assumed that if a character you control is in a tabletop, you have the rights
+ // to do actions in that tabletop
+ action.tabletopId = creature.tabletopId;
+
+ // Ensure that all the targeted creatures exist and share a tabletop
+ if (action.targetIds) for (const targetId of action.targetIds) {
+ const target = getCreature(targetId);
+ if (!target) {
+ throw new Meteor.Error('not-found', 'Target creature does not exist');
+ }
+ if (target.tabletopId !== action.tabletopId) {
+ throw new Meteor.Error('permission-denied', 'Target creature does not share a tabletop with the acting creature');
+ }
+ }
+
// First remove all other actions on this creature
// only do one action at a time, don't wait for this to finish
EngineActions.remove({ creatureId: action.creatureId });
diff --git a/app/imports/client/ui/log/CharacterLog.vue b/app/imports/client/ui/log/CharacterLog.vue
index 87ee5eee..bde5c6aa 100644
--- a/app/imports/client/ui/log/CharacterLog.vue
+++ b/app/imports/client/ui/log/CharacterLog.vue
@@ -41,7 +41,8 @@ import Creatures from '/imports/api/creature/creatures/Creatures';
import CreatureVariables from '/imports/api/creature/creatures/CreatureVariables';
import { assertEditPermission } from '/imports/api/creature/creatures/creaturePermissions';
import { parse, prettifyParseError } from '/imports/parser/parser';
-import resolve, { toString } from '/imports/parser/resolve';
+import resolve from '/imports/parser/resolve';
+import toString from '/imports/parser/toString';
import LogEntry from '/imports/client/ui/log/LogEntry.vue';
import { Tracker } from 'meteor/tracker'
@@ -110,14 +111,14 @@ export default {
if (this.history.length > 50) this.history.shift();
this.historyIndex = this.history.length;
},
- recalculate() {
+ async recalculate() {
this.inputHint = this.inputError = undefined;
if (!this.input) return;
let result;
try {
result = parse(this.input);
} catch (e){
- if (e.constructor.name === 'EndOfInputError'){
+ if (e?.constructor?.name === 'EndOfInputError'){
this.inputError = '...';
} else {
let error = prettifyParseError(e);
@@ -126,7 +127,7 @@ export default {
return;
}
try {
- let {result: compiled} = resolve('compile', result, this.variables);
+ let {result: compiled} = await resolve('compile', result, this.variables);
this.inputHint = toString(compiled);
return;
} catch (e){
@@ -146,7 +147,6 @@ export default {
}
}
},
- // @ts-ignore
meteor: {
logs() {
const filter = {};
diff --git a/app/imports/client/ui/log/TabletopLog.vue b/app/imports/client/ui/log/TabletopLog.vue
new file mode 100644
index 00000000..0b5feb5c
--- /dev/null
+++ b/app/imports/client/ui/log/TabletopLog.vue
@@ -0,0 +1,188 @@
+
+
+
+
+
+
+
+resolveimport { toString } from '/imports/parser/toString';
diff --git a/app/imports/client/ui/log/TabletopLogContent.vue b/app/imports/client/ui/log/TabletopLogContent.vue
new file mode 100644
index 00000000..33dfb2b7
--- /dev/null
+++ b/app/imports/client/ui/log/TabletopLogContent.vue
@@ -0,0 +1,63 @@
+
+
+
+
+ {{ content.name }}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/imports/client/ui/log/TabletopLogEntry.vue b/app/imports/client/ui/log/TabletopLogEntry.vue
new file mode 100644
index 00000000..77ebadaf
--- /dev/null
+++ b/app/imports/client/ui/log/TabletopLogEntry.vue
@@ -0,0 +1,59 @@
+
+
+
+
+
+
+ {{ creature.name && creature.name[0] || '?' }}
+
+
+
+
+ {{ creature.name }}
+
+
+
+
+
+
+
+
+
+
diff --git a/app/imports/client/ui/tabletop/TabletopLog.vue b/app/imports/client/ui/tabletop/TabletopLog.vue
deleted file mode 100644
index 5775dfcc..00000000
--- a/app/imports/client/ui/tabletop/TabletopLog.vue
+++ /dev/null
@@ -1,41 +0,0 @@
-
-
-
-
-
-
-
diff --git a/app/imports/client/ui/tabletop/TabletopRightDrawer.vue b/app/imports/client/ui/tabletop/TabletopRightDrawer.vue
index 4a82217d..59058bc5 100644
--- a/app/imports/client/ui/tabletop/TabletopRightDrawer.vue
+++ b/app/imports/client/ui/tabletop/TabletopRightDrawer.vue
@@ -10,7 +10,7 @@