@s2script/sdk 0.10.2 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/cli.js +23 -5
  2. package/entity.d.ts +14 -0
  3. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -1539,7 +1539,7 @@ var PLATFORM = "linuxsteamrt64";
1539
1539
  var ARG_KINDS = ["bool", "int", "float", "string", "vector", "entity"];
1540
1540
  var RET_KINDS = ["void", "bool", "int", "float", "entity"];
1541
1541
  var INT_CLASS_ARGS = /* @__PURE__ */ new Set(["bool", "int", "string", "vector", "entity"]);
1542
- var MAX_INT_ARGS = 5;
1542
+ var MAX_INT_ARGS = 9;
1543
1543
  var MAX_FLOAT_ARGS = 8;
1544
1544
 
1545
1545
  // src/gamedata/validate.ts
@@ -1604,8 +1604,12 @@ function validatePluginGamedata(gd, opts) {
1604
1604
  }
1605
1605
  const decl = rawDecl;
1606
1606
  const recv = decl.receiver;
1607
- if (!recv || recv.kind !== "entity") {
1608
- errs.push(`${where}: receiver.kind must be "entity" in v1`);
1607
+ if (!recv || recv.kind !== "entity" && recv.kind !== "none") {
1608
+ errs.push(`${where}: receiver.kind must be "entity" or "none"`);
1609
+ } else if (recv.kind === "none") {
1610
+ if (recv.via !== void 0) {
1611
+ errs.push(`${where}: receiver.kind "none" cannot carry a 'via' sub-object hop`);
1612
+ }
1609
1613
  } else if (recv.via !== void 0) {
1610
1614
  const via = recv.via;
1611
1615
  if (typeof via?.class !== "string" || typeof via?.field !== "string") {
@@ -1649,7 +1653,7 @@ function validatePluginGamedata(gd, opts) {
1649
1653
  else floats++;
1650
1654
  }
1651
1655
  if (ints > MAX_INT_ARGS) {
1652
- errs.push(`${where}: ${ints} integer-class args exceeds the max of ${MAX_INT_ARGS} (\`this\` occupies rdi)`);
1656
+ errs.push(`${where}: ${ints} integer-class args exceeds the max of ${MAX_INT_ARGS}`);
1653
1657
  }
1654
1658
  if (floats > MAX_FLOAT_ARGS) {
1655
1659
  errs.push(`${where}: ${floats} float args exceeds the max of ${MAX_FLOAT_ARGS}`);
@@ -1710,8 +1714,9 @@ function generateGamedataTypes(gd) {
1710
1714
  const lines = Object.keys(calls).sort().map((name) => {
1711
1715
  const decl = calls[name];
1712
1716
  const names = decl.argNames;
1717
+ const receiverless = decl.receiver?.kind === "none";
1713
1718
  const params = [
1714
- "self: EntityRef",
1719
+ ...receiverless ? [] : ["self: EntityRef"],
1715
1720
  ...decl.args.map((a2, i2) => `${names?.[i2] ?? `a${i2}`}: ${ARG_TS[a2]}`)
1716
1721
  ];
1717
1722
  return ` ${name}: (${params.join(", ")}) => ${RET_TS[decl.returns]};`;
@@ -5305,6 +5310,9 @@ function emitNavJs(model) {
5305
5310
  }
5306
5311
  for (const w of model.wrappers) {
5307
5312
  out.push(` function ${w.wrapper}(root, path, base) { this.root = root; this.path = path; this.base = base; }`);
5313
+ out.push(` ${w.wrapper}.prototype.notifyChanged = function () {`);
5314
+ out.push(` if (this.path.length > 0) this.root.notifyStateChanged(this.path[0]);`);
5315
+ out.push(` };`);
5308
5316
  out.push(` Object.defineProperties(${w.wrapper}.prototype, {`);
5309
5317
  for (const f of w.fields) {
5310
5318
  const own = `off(${S2(f.declaringClass)},${S2(f.rawName)})`;
@@ -5404,6 +5412,16 @@ function emitNavDts(model) {
5404
5412
  out.push(` */`);
5405
5413
  }
5406
5414
  out.push(`export interface ${w.wrapper} {`);
5415
+ out.push(
5416
+ " /**",
5417
+ " * Flag this sub-object for network replication after writing to it.",
5418
+ " *",
5419
+ " * Setters do not do this for you. For most targets it does not matter \u2014 the server reads the",
5420
+ " * field every tick \u2014 but anything a CLIENT renders (a scoreboard counter, say) will not update",
5421
+ " * until you call this.",
5422
+ " */",
5423
+ " notifyChanged(): void;"
5424
+ );
5407
5425
  for (const f of w.fields) {
5408
5426
  out.push(` ${f.navWritable ? "" : "readonly "}${f.propName}: ${TSTYPE[f.accessorKind]};`);
5409
5427
  }
package/entity.d.ts CHANGED
@@ -116,6 +116,20 @@ export declare class EntityRef {
116
116
  notifyStateChanged(offset: number): void;
117
117
  /** Raw identity-slot flags (engine m_flags), or null when stale/unavailable. Bit meanings are game-specific. */
118
118
  identityFlags(): number | null;
119
+ /**
120
+ * CLEAR identity-slot flag bits; returns the flags after the write, or null when stale.
121
+ *
122
+ * Clear-only by design: `mask` names bits to DROP and nothing can be raised, because handing a
123
+ * plugin the ability to set arbitrary identity bits is a far larger foot-gun than the one this
124
+ * closes. The invalid-ehandle bit is refused outright — a dead slot must never be presentable as
125
+ * live.
126
+ *
127
+ * The case this exists for is the staging bit. `setModel` asserts that an entity is NOT in the
128
+ * staging list, and a created-but-unspawned entity is, so the create -> setModel -> spawn ordering
129
+ * (what CS2's own body spawner does) is otherwise unavailable and the alternatives produce
130
+ * half-initialised entities that clients fail to copy.
131
+ */
132
+ clearIdentityFlags(mask: number): number | null;
119
133
  /** DispatchSpawn this created entity. With keyvalues, the entity's own Spawn() parses them (the
120
134
  * SourceMod DispatchKeyValue / CSSharp DispatchSpawn(kv) mechanism). Returns false if stale,
121
135
  * unresolved, or the keyvalue map is rejected (non-finite number, unsupported value type, empty
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@s2script/sdk",
3
- "version": "0.10.2",
3
+ "version": "0.11.0",
4
4
  "license": "MIT OR Apache-2.0",
5
5
  "description": "s2script SDK — the builtin capability types (imported as @s2script/sdk/entity, …) plus the `s2s` CLI for building Source 2 / CS2 plugins.",
6
6
  "type": "module",