42 Commits

Author SHA1 Message Date
claridtimo b792875787 Merge pull request #1 from claridtimo/add-pr-target-hook
Add fork-PR-target hook (verbatim from bang-game #63) + battery CI gate
2026-07-10 16:38:36 +12:00
Claridtimo 3b6235d684 Add fork-PR-target hook (verbatim from bang-game #63) + battery CI gate
Propagates the PreToolUse enforce-pr-target.sh backstop (byte-identical to
claridtimo/bang-game master) that denies any `gh pr create` not explicitly
aimed at claridtimo/*, guarding against accidental PRs on the greyhavens
upstream fork parent. Ships the 103-case regression battery and a CI job
(.github/workflows/hook-battery.yml) that gates it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E3cmNbMM8gGpprBy8kcNuV
2026-07-10 16:33:01 +12:00
Claridtimo 9f4f2aade8 fix: Percentiler returns 100 for the max score; build against local-stack SNAPSHOTs
getPercentile used 'value > _max', so the highest score ever seen returned
~99 instead of 100, contradicting the method's javadoc. Use '>=' to match
the documented contract. Also pin sibling deps to local SNAPSHOTs
(ooo-util 1.6, narya 1.20, nenya/nenya-tools 1.7.3, depot 1.9).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 11:39:45 +12:00
Ray J. Greenwell 9a1542f1c9 Make tests run headlessly. 2026-05-13 15:04:32 -07:00
Ray J. Greenwell 0dc0982942 Stop depending on narya's dependency on jsr205. Use modern @Generated. 2026-04-27 15:03:20 -07:00
Ray J. Greenwell 44d0d61cec Specify UTF-8 file encoding to maven. 2026-03-21 15:35:29 -07:00
Michael Bayne bb6f0be29a [maven-release-plugin] prepare for next development iteration 2025-10-21 13:22:34 -07:00
Michael Bayne d4a1665550 [maven-release-plugin] prepare release vilya-1.7.1 2025-10-21 13:22:31 -07:00
Michael Bayne 4b3598093c Fix serialization of Cluster.
We can no longer rely on the ability to reflectively access the private members
of java.awt.Rectangle. Modern JVMs are stricter about enforcing cross-module
private access. Prudes.
2025-08-23 18:17:52 -07:00
Michael Bayne b23d91cfd4 [maven-release-plugin] prepare for next development iteration 2025-07-04 15:12:48 -07:00
Michael Bayne 279206ce88 [maven-release-plugin] prepare release vilya-1.7 2025-07-04 15:12:46 -07:00
Michael Bayne 1f49dd6c40 Javadoc fixes. 2025-07-04 15:12:11 -07:00
Michael Bayne 44928f4084 Depend on shipped libraries. 2025-07-04 15:10:22 -07:00
Michael Bayne 865c06d916 Update to new Sonatype deployment bits.
Other plugin upgrades & modernizations.
2025-07-04 15:10:04 -07:00
Michael Bayne f6fd0d2cb5 Ignore Maven target directories. 2025-02-05 11:01:19 -08:00
Michael Bayne 702c2d697f Update dependencies, target Java 10. 2025-02-05 11:01:10 -08:00
Michael Bayne 8bbdabe5d6 Provide a method for calling playerReady.
The GameManager is set up to handle it, so let's not fuss too much about the
fact that we're using a dynamically typed mechanism to send a message from
client to server.
2025-02-03 13:46:00 -08:00
Michael Bayne 074ef440b8 Target JDK 1.6. 2018-03-27 14:22:05 -07:00
Michael Bayne 486d4c80b1 Move aslib module to profile. 2016-12-17 17:05:28 -08:00
Michael Bayne 5a6a960cd9 Inherit depends from nenyalib; added flexgroup repo. 2015-03-17 09:23:41 -07:00
Michael Bayne fe05edab42 Updated aslib build. 2015-03-17 09:08:13 -07:00
Michael Bayne a0e8a23b9d Cache Maven stuffs on Travis. 2015-03-17 08:54:38 -07:00
Michael Bayne 968fb80a7b Only build master. 2015-03-14 10:47:55 -07:00
Michael Bayne 84b4ef98b4 Wire up a Travis build. 2015-03-14 10:09:58 -07:00
Michael Bayne cbaaa514e1 [maven-release-plugin] prepare for next development iteration 2015-03-14 10:06:35 -07:00
Michael Bayne 6a65aa773e [maven-release-plugin] prepare release vilya-1.6 2015-03-14 10:06:32 -07:00
Michael Bayne aca72a52f5 Nix vestigial ooo-repo. 2015-03-14 10:02:43 -07:00
Michael Bayne 380e78dd98 Javadoc fixes. 2015-03-14 10:01:07 -07:00
Michael Bayne 94c9852b2a Nix non-existent (Github) ooo-maven repo.
I think this was auto-converted from Google Code, but I'm pretty sure it's no
longer needed (if it ever was). Famous last words.
2015-03-14 09:57:25 -07:00
Michael Bayne 3934c99b38 Version bumps in prep for release. 2015-03-14 09:56:49 -07:00
Michael Bayne 2199bfd21a Eliminate use of TestUtil. 2015-03-14 09:54:42 -07:00
Jamie Doornbos 95294dd6fe Profile to deploy locally. 2014-11-14 13:23:21 -08:00
Jamie Doornbos 2c26d6405a Move eclipse stuff to avoid warnings when building javadoc and sources. 2014-11-14 13:22:32 -08:00
Ray J. Greenwell f9b696b994 Back to snapshot narya.
This is a public library, we need to depend on publicly available versions.
2014-04-01 16:45:59 -07:00
Ray J. Greenwell dc16869d5c Preparing for 1.6-SNAPSHOT development. 2014-04-01 16:43:59 -07:00
Ray J. Greenwell bb09b0d4a0 Preparing for vilya-parent-1.5.2 release. 2014-04-01 16:40:39 -07:00
Ray J. Greenwell 549afd19e3 Depend on narya 1.14.3
I think I'll need to change this back after the internal release.
2014-04-01 16:39:50 -07:00
Mark Johnson 7b0c77bf5d The deploy was not deploying the core library, copy the narya setup 2014-03-21 19:11:23 -07:00
Mark Johnson a8ff54915a - Update vilya to snapshot narya and make necessary changes for it to build 2014-03-21 15:53:32 -07:00
Pär Winzell abb3a1a523 Actually update build.xml to handle new parent/core/aslib structure. 2013-05-13 14:02:04 -05:00
Pär Winzell b670c92eba Update build.xml to handle new parent/core/aslib structure. 2013-05-10 20:17:40 -05:00
Michael Bayne a21991c7e9 [maven-release-plugin] prepare for next development iteration 2013-05-08 11:13:05 -07:00
53 changed files with 904 additions and 237 deletions
+397
View File
@@ -0,0 +1,397 @@
#!/usr/bin/env bash
#
# PreToolUse(Bash) hook — hard backstop against opening PRs on the upstream fork parent.
#
# This repo (and the sibling fork clones) is a FORK of greyhavens/*. `gh pr create` with no
# -R/--repo defaults its base repo to the fork PARENT, so a bare invocation opens a PR against
# greyhavens by mistake (it has, more than once). The CLAUDE.md guidance + `gh repo set-default`
# are soft — this hook is the enforced version: it DENIES any `gh pr create` not explicitly aimed
# at a claridtimo/* repo, and feeds the reason back so the model just re-runs correctly.
#
# WHY it parses argv instead of grepping raw text: earlier revisions matched the raw command
# string, which mis-read a `-R claridtimo/…` substring inside a --title/--body (false allow) and
# split on shell operators inside a quoted value (false deny). In THIS repo those aren't
# pathological — we routinely write PRs whose titles/bodies contain gh examples, shell snippets,
# and ordinary apostrophes ("don't"). So the precise path tokenizes the command the way a shell
# actually would, with Python's `shlex` (the reference POSIX shell lexer — it handles nested quote
# types, e.g. an apostrophe inside a double-quoted title, which a bash/xargs tokenizer cannot do
# portably). Only a real `-R`/`--repo` flag token — never text inside a quoted value — counts as a
# target, and clause boundaries are only real operator tokens.
#
# Dependency posture: a PreToolUse hook that ERRORS is treated as non-blocking, so a hard
# dependency would silently REMOVE the guard on a box that lacks it. The precise path uses python3
# (stdlib only: json + shlex), guarded by `command -v`. If python3 is absent OR the command can't
# be parsed, the script DEGRADES to a conservative text check that never false-denies a targeted PR
# and still catches the common bare omission. It never exits non-zero; blocking is via the JSON
# "deny", not the exit code.
INPUT=$(cat)
# Cheap prefilter before anything else: a deny can only ever fire on a clause containing the
# tokens `gh` … `pr` … `create`, and no shell QUOTING can produce those tokens without the letters
# "gh" and "create" appearing verbatim in the raw input (JSON never escapes letters). So for the
# overwhelmingly common unrelated Bash call, skip the python3 spawn (and the greps) entirely.
# A deliberately backslash-escaped `crea\te` slips past this — deliberate evasion is out of the
# threat model (the hook guards against ACCIDENTAL omission), same class as shell aliases.
case "$INPUT" in *gh*) : ;; *) exit 0 ;; esac
case "$INPUT" in *create*) : ;; *) exit 0 ;; esac
emit_deny() {
cat <<'JSON'
{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":"PRs must target the claridtimo fork, never upstream greyhavens. This `gh pr create` has no explicit claridtimo target (as a real -R/--repo FLAG, not text inside a --title/--body), so gh would default its base repo to the upstream fork parent. Re-run with the flag aimed at claridtimo, e.g.:\n gh pr create -R claridtimo/<repo> --base <branch> --head <feature-branch> ...\nA target on a different &&-chained command, or one that only appears inside a quoted title/body, does NOT count."}}
JSON
}
# ---- Precise path: python3 (stdlib json + shlex) --------------------------------------------
# Emits exactly one word on stdout: DENY, ALLOW, or FALLBACK. Any crash / missing python / weird
# exit prints nothing, and we fall through to the degraded check below. Never trusts a partial parse.
if command -v python3 >/dev/null 2>&1; then
# Input goes via env var (HOOK_INPUT), leaving stdin free for the heredoc'd script itself.
verdict=$(HOOK_INPUT="$INPUT" python3 - <<'PY' 2>/dev/null
import json, re, shlex, sys, os
try:
cmd = json.loads(os.environ.get("HOOK_INPUT", "")).get("tool_input", {}).get("command", "")
except Exception:
print("FALLBACK"); sys.exit(0)
if not cmd:
print("FALLBACK"); sys.exit(0)
def scan_subst(s, i, opener):
# Scan a command-substitution / subshell body starting at s[i] (the char AFTER the opener).
# opener "(" ends at its nesting-matched ")" (honoring quotes and backslashes, the way bash
# re-parses the inside of $() as a fresh context); opener backtick ends at the next unescaped
# backtick (backticks don't nest unescaped). Unterminated bodies consume to end-of-string —
# that input is malformed shell that bash would refuse to run, so any verdict is safe.
# Returns (content, index_after_closer).
q2 = None
depth = 1
buf = []
j, n = i, len(s)
while j < n:
c = s[j]
if q2 is not None:
buf.append(c)
if c == q2:
q2 = None
elif c == "\\" and q2 == '"' and j + 1 < n:
buf.append(s[j + 1]); j += 2; continue
j += 1; continue
if c == "\\" and j + 1 < n:
buf.append(c); buf.append(s[j + 1]); j += 2; continue
if c in ("'", '"'):
q2 = c; buf.append(c); j += 1; continue
if opener == "(":
if c == "(":
depth += 1
elif c == ")":
depth -= 1
if depth == 0:
return "".join(buf), j + 1
elif c == chr(96):
return "".join(buf), j + 1
buf.append(c); j += 1
return "".join(buf), n
def to_separators(s, depth=0):
# shlex.split() is a quote-aware WORD splitter, not a shell control-operator parser: it only
# treats & | ; and newlines as separators when whitespace already surrounds them. Real bash
# splits on them regardless (`echo a&&echo b` is two commands). So a quote-aware pre-pass
# rewrites every shell metacharacter the way a shell lexer would:
# - unquoted control operators (& | ; newline) become " ; " — a clause boundary;
# - unquoted redirections (< >, and an & glued to one: 2>&1, &>f) become " " — a TOKEN
# boundary but NOT a clause boundary, since a redirect doesn't end the command
# (`gh pr create>out` must still be seen as a create, and a `-R` AFTER `2>&1` is still
# part of the same clause);
# - an unquoted # at word start drops the rest of the line (a comment; a mid-word # stays
# literal, matching shell) — a `-R claridtimo/…` living only in a comment can't count;
# - command substitutions and subshells — $(…), `…`, bare (…) — are EXTRACTED: the body is
# removed from the enclosing command (which stays contiguous, so a substitution used as a
# flag value can't split a targeted create away from its -R) and appended as its own
# " ; "-separated clause, recursively pre-passed, so an inner `gh pr create` is judged on
# its own. This applies inside DOUBLE quotes too — bash executes $()/backticks there
# (only single quotes are inert), so a create hidden in a --title "… $(gh pr create …)"
# is still caught (11th review round).
# Other quoted metacharacters (a --body/title) are preserved; a backslash-escaped one is
# preserved for shlex to handle. Remaining blind spots, all deliberate-evasion class (out of
# threat model — the guard is against ACCIDENTAL omission): shell aliases, backslash-escaped
# letters (`crea\te`), ${var@P}-style expansion tricks. Heredoc BODIES are not
# quote-delimited, so a bare `gh pr create` EXAMPLE inside a heredoc'd --body-file can
# false-DENY — the safe direction; re-run with the example inside a quoted --body or a file.
if depth > 10:
raise ValueError("substitution nesting too deep") # → caller falls back (conservative)
out = []
extracted = []
q = None
i, n = 0, len(s)
while i < n:
c = s[i]
if q is not None: # inside a quote
if q == '"':
# bash still runs $() and backticks inside double quotes — extract them
if c == "$" and i + 1 < n and s[i + 1] == "(":
body, i = scan_subst(s, i + 2, "(")
extracted.append(body); continue
if c == chr(96):
body, i = scan_subst(s, i + 1, chr(96))
extracted.append(body); continue
if c == "\\" and i + 1 < n:
if s[i + 1] == "\n": # line continuation: bash removes both chars
i += 2; continue
out.append(c); out.append(s[i + 1]); i += 2; continue
out.append(c)
if c == q:
q = None
i += 1; continue
if c in ("'", '"'):
q = c; out.append(c); i += 1; continue
if c == "\\" and i + 1 < n: # unquoted backslash escapes the next char
if s[i + 1] == "\n": # line continuation: bash removes both chars,
i += 2; continue # joining the surrounding text (16th round)
out.append(c); out.append(s[i + 1]); i += 2; continue
if c == "#" and (i == 0 or s[i - 1] in " \t&|;()<>\n\r" or s[i - 1] == chr(96)):
while i < n and s[i] != "\n": # comment: shell ignores to end of line
i += 1
continue
# The " __subst__ " placeholder keeps the token count intact: a substitution used as a
# flag VALUE (--title $(gen) -R …) must still occupy the value slot, or the value flag
# would consume the following -R as its value and false-deny a targeted create.
if c == "$" and i + 1 < n and s[i + 1] == "(":
body, i = scan_subst(s, i + 2, "(")
extracted.append(body); out.append(" __subst__ "); continue
if c == "(": # bare subshell / grouping
body, i = scan_subst(s, i + 1, "(")
extracted.append(body); out.append(" __subst__ "); continue
if c == chr(96):
body, i = scan_subst(s, i + 1, chr(96))
extracted.append(body); out.append(" __subst__ "); continue
if c in "<>":
out.append(" ") # redirection: token boundary, not clause boundary
i += 1; continue
if c == "&" and ((i + 1 < n and s[i + 1] in "<>") or (i > 0 and s[i - 1] in "<>")):
out.append(" ") # & that is part of a redirect (2>&1, &>f, <&0)
i += 1; continue
if c == "|":
if i + 1 < n and s[i + 1] == "|":
out.append(" ; "); i += 2; continue # || is OR — a plain clause boundary
if i + 1 < n and s[i + 1] == "&":
out.append(" __pipe__ "); i += 2; continue # |& pipes stdout+stderr
out.append(" __pipe__ "); i += 1; continue # a real pipe: the next clause reads
# this clause's stdout as ITS stdin — judge() uses this to catch `echo … | bash`
out.append(" ; " if c in "&;)\n\r" else c) # stray ")" = malformed; split conservatively
i += 1
for body in extracted:
out.append(" ; ")
out.append(to_separators(body, depth + 1))
return "".join(out)
# After the pre-pass every unquoted separator is a lone " ; " or " __pipe__ ", so these are the
# only clause-boundary tokens shlex can produce here (operator text inside quotes stays part of
# its value token). The pipe stays distinct because `echo "gh pr create …" | bash` EXECUTES the
# echoed text (17th review round) — judge() carries an echo/printf clause's payload across a
# pipe boundary and judges it when the receiving clause is a shell reading stdin (no -c).
OPS = {";", "__pipe__"}
# gh flags that consume the NEXT token as an opaque value; that value must never be read as an
# operator or a flag. -R/--repo are handled explicitly below (their value is what we inspect).
VALUE_FLAGS = {"-t","--title","-b","--body","-F","--body-file","-B","--base","-H","--head",
"-l","--label","-a","--assignee","-r","--reviewer","-m","--milestone",
"-p","--project","-T","--template","--recover"}
# Shell-wrapper basenames whose `-c <string>` argument is itself a command (15th review round:
# `bash -c "gh pr create …"` is an ORDINARY idiom, inside the accidental-omission threat model —
# and shlex collapsing the string to one opaque token had silently ALLOWED it, a regression vs
# the old raw-grep hook). Such strings are recursively judged as commands, as is everything
# after `eval` (which concatenates its args and executes them). Not covered: `ssh host "…"` /
# `su -c` (remote/privileged contexts our agents never route gh through — and the degraded grep
# below still catches those textually) and non-shell interpreters (`python -c 'os.system(…)'`,
# deliberate-evasion class).
SHELLS = {"bash", "sh", "zsh", "dash", "ksh"}
# Wrappers that keep the following word at command position (their own options/durations are
# skipped by the dash/numeric rules at the use site).
PREFIXES = {"sudo", "doas", "env", "nohup", "setsid", "command", "exec", "time", "xargs",
"nice", "ionice", "stdbuf", "timeout", "strace", "ltrace"}
def judge(cmd, depth=0):
# Returns True if any clause anywhere in cmd (including inside bash -c / eval strings) is an
# untargeted `gh pr create`. Raises ValueError on unparseable input → caller falls back.
if depth > 5:
raise ValueError("wrapper nesting too deep")
toks = shlex.split(to_separators(cmd)) # POSIX tokenization; unquoted newlines act as ";"
deny = False
gh = pr = create = targeted = False
cmd_pos = True # scanning the clause's COMMAND position (vs its arguments)
printed = None # args of the echo/printf clause just scanned (they were PRINTED)
piped = None # that payload, if the boundary we just crossed was a pipe
i, n = 0, len(toks)
while i < n:
t = toks[i]
if t in OPS: # clause boundary: judge the clause we just finished
if gh and pr and create and not targeted:
deny = True
# an echo/printf payload only survives across a PIPE — `echo … | bash` feeds it to
# the shell's stdin, while `echo …; bash` prints and moves on (17th review round)
piped = printed if t == "__pipe__" else None
printed = None
gh = pr = create = targeted = False
cmd_pos = True
i += 1
continue
if cmd_pos:
if re.match(r"^[A-Za-z_][A-Za-z0-9_]*=", t):
i += 1; continue # leading VAR=val assignment — still at command position
base = t.rsplit("/", 1)[-1]
# command-position-preserving prefixes and their option/duration arguments: after
# `sudo`/`env`/`timeout 5`/`nice -n 10`/`xargs`/… the NEXT word is still the invoked
# command
if base in PREFIXES or re.match(r"^[0-9]+[smhd]?$", t):
i += 1; continue
if t.startswith("-"):
# a prefix option may take a VALUE (`sudo -u root`, `xargs -I {}`): consume the
# following plain word as that value so the wrapper AFTER it is still judged at
# command position (20th review round: `sudo -u root bash -c "…"` bypassed the
# wrapper check when `root` closed command position). The word is NOT consumed
# when it is itself a shell/eval — a no-value option directly before the command
# (`env -i bash -c "…"`) is likelier than a value named after a shell.
nxt = toks[i + 1] if i + 1 < n and toks[i + 1] not in OPS else None
if nxt and not nxt.startswith("-") and not re.match(r"^[0-9]+[smhd]?$", nxt) \
and nxt.rsplit("/", 1)[-1] not in SHELLS and nxt != "eval" \
and not re.match(r"^[A-Za-z_][A-Za-z0-9_]*=", nxt):
i += 2; continue
i += 1; continue
cmd_pos = False
# this token IS the clause's invoked command. Wrapper/print semantics apply ONLY
# here: bash/eval/echo as a mere ARGUMENT (`grep eval -c "gh pr create test" f`,
# `… | grep bash`) neither executes nor prints anything (19th review round — those
# shapes were false-denied when wrappers matched anywhere in the clause).
if base in ("echo", "printf"):
# a print clause never EXECUTES its arguments — skip it whole, but REMEMBER
# them: if this clause pipes into a stdin-reading shell, the printed text
# becomes commands after all (16th/17th review rounds)
args = []
i += 1
while i < n and toks[i] not in OPS:
args.append(toks[i]); i += 1
printed = args
continue
if base in SHELLS:
# scan this clause for -c (alone or in a cluster like -lc); its argument is a
# command in its own right
j = i + 1
saw_c = False
while j < n and toks[j] not in OPS:
f = toks[j]
if f.startswith("-") and not f.startswith("--") and "c" in f:
saw_c = True
if j + 1 < n and toks[j + 1] not in OPS and judge(toks[j + 1], depth + 1):
deny = True
break
j += 1
# no -c: the shell reads stdin — if an echo/printf payload was piped in, judge
# it (`cat file | bash` etc. remain unjudgeable: unknown content, degraded grep
# only; multi-hop pipes like `echo … | tee f | bash` drop the payload — accepted)
if not saw_c and piped:
if judge(" ".join(piped), depth + 1):
deny = True
piped = None
# fall through: the shell token itself still walks the generic checks below
if t == "eval": # eval concatenates its args and executes them
j = i + 1
args = []
while j < n and toks[j] not in OPS:
args.append(toks[j]); j += 1
if args and judge(" ".join(args), depth + 1):
deny = True
i = j # the args were judged in recursion, not in this walk
continue
elif t in ("-exec", "-execdir", "-ok"):
cmd_pos = True # find(1): the token after -exec is an invoked command
i += 1
continue
if t == "gh" or t.endswith("/gh"): # bare `gh` or a full/relative path like /usr/bin/gh
gh = True
elif t == "pr" and gh:
pr = True
elif t == "create" and pr:
create = True
# Each -R/--repo SETS the target verdict from its own value — last flag wins, matching gh's
# repeated-flag semantics (a later -R greyhavens/... after -R claridtimo/... must UN-target).
if t in ("-R", "--repo"): # target flag; value is the next token
if i + 1 < n and toks[i + 1] not in OPS:
targeted = toks[i + 1].lower().startswith("claridtimo/")
i += 2
else: # dangling flag at a clause boundary: no value, and the
targeted = False # boundary token must still be processed (18th round)
i += 1
continue
if t.startswith("-R=") or t.startswith("--repo="):
targeted = t.split("=", 1)[1].lower().startswith("claridtimo/")
elif t.startswith("-R") and len(t) > 2: # -Rclaridtimo/… glued short form
targeted = t[2:].lower().startswith("claridtimo/")
if t in VALUE_FLAGS: # next token is this flag's opaque value — skip it,
# unless it is a clause boundary (a dangling value flag must not swallow the OPS
# token — the clause-reset/deny-check there is what the state machine relies on)
i += 2 if (i + 1 < n and toks[i + 1] not in OPS) else 1
continue
i += 1
if gh and pr and create and not targeted:
deny = True
return deny
try:
deny = judge(cmd)
except ValueError:
print("FALLBACK"); sys.exit(0) # unbalanced quotes / absurd nesting → degraded path decides
print("DENY" if deny else "ALLOW")
PY
)
case "$verdict" in
DENY) emit_deny; exit 0 ;;
ALLOW) exit 0 ;;
*) : ;; # FALLBACK / empty (python crashed or missing) → degraded path below
esac
fi
# ---- Degraded path: no python3, or the command couldn't be parsed — conservative -------------
# Deny only the clear-cut case: a `gh pr create` with no -R/--repo flag ADJACENT to a
# "claridtimo/" value anywhere in the input. The adjacency requirement matters: the hook stdin
# is the whole PreToolUse payload (cwd, transcript_path, …), so a bare "claridtimo/" substring
# test would false-ALLOW every bare create on a box whose checkout PATH contains "claridtimo"
# (10th review round) — and the degraded path exists precisely for such less-set-up boxes.
# Requiring the flag form still NEVER false-denies a targeted PR (every targeted create carries
# `-R claridtimo/…`, `-R=…`, `-Rclaridtimo/…`, or a --repo equivalent — all matched below).
#
# KNOWN, ACCEPTED under-blocks (12th review round) — this path prioritizes never-false-denying
# over completeness, and cannot have both without a real tokenizer:
# - a flag-shaped `-R claridtimo/…` inside a quoted --title/--body satisfies the check;
# - a real target on an UNRELATED chained clause (`gh pr view -R claridtimo/x; gh pr create`)
# satisfies a bare create elsewhere in the same input.
# Clause-scoping this fallback with sed/grep was TRIED (the v2 hook that #61 merged) and
# reverted: without a quote-aware tokenizer, operators inside quoted PR bodies split clauses
# wrongly and false-denied real targeted creates — the exact bug class this rework removes.
# Both shapes are pinned in test-enforce-pr-target.sh (degraded-ALLOW vs precise-DENY) so a
# future change flipping either direction fails the battery. Every box we actually use has
# python3; this is a last-resort backstop, and `gh repo set-default` (bin/setup-gh-defaults)
# remains the primary guard.
# [Cc]laridtimo: GitHub owner names are case-insensitive (the precise path lowercases the whole
# value; here only the realistic accidental variant, a capitalized C, is matched — the flag part
# stays case-sensitive so -r/--reviewer values are not read as targets).
# SEP: a separator between tokens can be REAL whitespace or its JSON-ESCAPED form — the hook
# stdin is a JSON document, so a newline/tab inside the command arrives as the two characters
# \n / \t and a shell line-continuation backslash as \\ (16th review round: a backslash-
# continued `gh pr \<newline>create` must still match on exactly the boxes this fallback
# protects; the same class must count as flag/value adjacency or a continued targeted create
# would false-deny).
SEP='([[:space:]]|\\n|\\t|\\r|\\\\)'
# Scope the greps to the "command" FIELD when extractable: the payload also carries description/
# cwd/transcript_path, and a description that MENTIONS the intended target must not satisfy the
# check for a command that forgot the flag — nor should a description quoting `gh pr create`
# false-deny an unrelated command (18th review round). The ERE walks escaped chars inside the
# JSON string value; if extraction yields nothing (unexpected payload shape), fall back to the
# whole input, which errs toward DENY only for inputs that contain the create phrase anyway.
# (grep only — the degraded path must not depend on anything beyond bash/grep/printf/cat)
SCOPE=$(printf '%s' "$INPUT" | grep -oE '"command"[[:space:]]*:[[:space:]]*"(\\.|[^"\\])*"')
[ -n "$SCOPE" ] || SCOPE="$INPUT"
if printf '%s' "$SCOPE" | grep -qE "gh${SEP}+pr${SEP}+create" \
&& ! printf '%s' "$SCOPE" | grep -qE "(-R|--repo)(=|${SEP})*[Cc]laridtimo/"; then
emit_deny
fi
exit 0
+185
View File
@@ -0,0 +1,185 @@
#!/usr/bin/env bash
#
# Regression battery for enforce-pr-target.sh — run after ANY edit to the hook:
# .claude/hooks/test-enforce-pr-target.sh
# Exercises every bypass/false-deny class found across the #61/#63 review rounds, plus the
# degraded (no-python3) path via a stripped PATH. Exits non-zero on any failure.
# (Test-only dependency on python3 for safe JSON construction; the DEGRADED section still
# tests the hook itself without python3 on PATH.)
set -u
HOOK="$(cd "$(dirname "$0")" && pwd)/enforce-pr-target.sh"
pass=0; fail=0
run_case() { # expect(DENY|ALLOW) command [pathenv]
local expect="$1" cmd="$2" pathenv="${3:-$PATH}"
local input verdict out
input=$(python3 -c 'import json,sys; print(json.dumps({"tool_input":{"command":sys.argv[1]}}))' "$cmd")
out=$(printf '%s' "$input" | env PATH="$pathenv" bash "$HOOK")
_judge "$expect" "$out" "$cmd"
}
run_case_raw() { # expect(DENY|ALLOW) raw-json-input label [pathenv]
local expect="$1" input="$2" label="$3" pathenv="${4:-$PATH}"
local out
out=$(printf '%s' "$input" | env PATH="$pathenv" bash "$HOOK")
_judge "$expect" "$out" "$label"
}
_judge() {
local expect="$1" out="$2" label="$3" verdict
if printf '%s' "$out" | grep -q '"permissionDecision":"deny"'; then verdict=DENY; else verdict=ALLOW; fi
if [ "$verdict" = "$expect" ]; then
pass=$((pass+1)); echo "ok $expect $label"
else
fail=$((fail+1)); echo "FAIL want=$expect got=$verdict $label"
fi
}
echo "=== DENY: untargeted creates, incl. every historical bypass class ==="
run_case DENY 'gh pr create --title foo --body bar'
run_case DENY 'gh pr create>out --title foo' # glued redirect (round 9)
run_case DENY 'gh pr create</dev/null' # glued stdin redirect
run_case DENY 'gh pr create 2>&1' # redirect combo, still untargeted
run_case DENY 'gh pr create --title foo # -R claridtimo/x' # target only in a comment
run_case DENY 'git push && gh pr create -t x'
run_case DENY 'git push&&gh pr create -t x' # glued operator (v6)
run_case DENY 'url=$(gh pr create -t x)' # command substitution (v9)
run_case DENY 'echo `gh pr create -t x`' # backtick substitution (v9)
run_case DENY 'gh pr create --title "use -R claridtimo/bang-game"' # target text inside a title (v3)
run_case DENY 'gh pr create -R greyhavens/bang-game -t x'
run_case DENY 'gh pr create -R claridtimo/x -R greyhavens/y' # last -R wins (v8)
run_case DENY '/usr/bin/gh pr create -t x' # gh by path (v7)
run_case DENY $'git status\ngh pr create -t x' # multi-line (v6)
run_case DENY 'gh pr create -R claridtimo/x && gh pr create -t y' # second clause untargeted
echo "=== ALLOW: targeted creates + unrelated commands ==="
run_case ALLOW 'gh pr create -R claridtimo/bang-game -t x -b y'
run_case ALLOW 'gh pr create --repo claridtimo/bang-game -t x'
run_case ALLOW 'gh pr create --repo=claridtimo/bang-game -t x'
run_case ALLOW 'gh pr create -Rclaridtimo/bang-game -t x' # glued short form
run_case ALLOW "gh pr create -R claridtimo/x --body 'a && b; gh pr create'" # ops inside quoted body (v3)
run_case ALLOW "gh pr create -R claridtimo/x --title \"don't break\"" # apostrophe (v5)
run_case ALLOW 'gh pr create -R claridtimo/x > /tmp/out' # redirect after target
run_case ALLOW 'gh pr create > /tmp/out -R claridtimo/x' # redirect before target
run_case ALLOW 'gh pr create 2>&1 -R claridtimo/x' # &-in-redirect (was a v9 false-deny)
run_case ALLOW 'gh pr create>log -R claridtimo/x' # glued redirect, still targeted
run_case ALLOW 'gh pr list'
run_case ALLOW 'gh pr view 63 -R claridtimo/bang-game'
run_case ALLOW 'gh pr merge 63 -R claridtimo/bang-game --merge'
run_case ALLOW 'git commit -m "gh pr create later"' # words in a -m value
run_case ALLOW 'echo done' # prefilter early-exit (no gh)
run_case ALLOW './gradlew deploy && echo high create' # prefilter passes, no gh token
run_case ALLOW 'gh pr create --title=has#hash -R claridtimo/x' # mid-word # is NOT a comment
run_case ALLOW 'gh repo create claridtimo/new-repo' # repo create is not pr create
echo "=== Substitution extraction (round 11): inner commands judged, outer kept contiguous ==="
run_case DENY 'gh pr create -R claridtimo/x -t "notes: $(gh pr create -t oops)"' # dq-hidden create
run_case DENY 'gh pr create -R claridtimo/x -t "notes: `gh pr create -t oops`"' # dq-hidden backtick
run_case DENY 'gh pr create -R claridtimo/x -b "$(echo $(gh pr create -t deep))"' # nested substitution
run_case ALLOW 'gh pr create --title "cost $(compute) done" -R claridtimo/x' # dq subst mid-command
run_case ALLOW 'gh pr create --title $(gen-title) -R claridtimo/x' # unquoted subst mid-command
run_case ALLOW 'gh pr create -R claridtimo/x --title "(parens) are fine"' # plain parens in dq
run_case DENY 'gh pr create --recover -Rclaridtimo/x.txt' # --recover value is opaque, not a -R
echo "=== Shell wrappers (round 15): bash -c / eval strings are commands too ==="
run_case DENY 'bash -c "gh pr create -t x"'
run_case DENY "bash -lc 'gh pr create -t x'" # combined flag cluster
run_case DENY "sh -c 'gh pr create -t x'"
run_case DENY "/bin/bash -c 'gh pr create -t x'" # shell by path
run_case DENY 'eval "gh pr create -t x"'
run_case DENY 'eval gh pr create -t x' # eval with unquoted args
run_case DENY "nohup bash -c 'gh pr create -t x' &"
run_case ALLOW "bash -c 'gh pr create -R claridtimo/x -t y'" # targeted inside the wrapper
run_case ALLOW 'eval "gh pr view 63 -R claridtimo/x" && echo create' # wrapper runs no create
run_case ALLOW "bash -c 'echo ghost created'" # words, not tokens
echo "=== Wrappers only at command position (round 19) ==="
run_case ALLOW 'grep eval -c "gh pr create test" file.txt' # eval as a grep ARG
run_case ALLOW 'echo "gh pr create -t x" | grep bash' # bash as a grep ARG
run_case ALLOW 'git log --grep eval -- "gh pr create notes.md"' # wrapper words in args
run_case DENY 'sudo bash -c "gh pr create -t x"' # prefix keeps command position
run_case DENY 'timeout 5 bash -c "gh pr create -t x"' # numeric prefix arg
run_case DENY 'xargs bash -c "gh pr create -t x"'
run_case DENY 'find . -name "*.md" -exec bash -c "gh pr create -t x" \;' # -exec re-arms
run_case DENY 'VAR=1 env bash -lc "gh pr create -t x"'
run_case DENY 'sudo -u root bash -c "gh pr create -t x"' # option VALUE before the shell (round 20)
run_case DENY 'xargs -I {} bash -c "gh pr create -t x"' # unglued option value
run_case DENY 'env -i bash -c "gh pr create -t x"' # no-value option directly before shell
run_case DENY 'sudo -u root gh pr create -t x' # generic detection through prefixes
run_case ALLOW 'sudo -u root bash -c "gh pr create -R claridtimo/x"' # targeted inside sudo-wrapped shell
run_case ALLOW 'sudo -u root gh pr create -R claridtimo/x'
echo "=== echo/printf clauses print, not execute (round 16) ==="
run_case ALLOW 'echo bash -c "gh pr create -t x"' # echoed text, never run
run_case ALLOW 'echo gh pr create' # literal words to stdout
run_case ALLOW 'printf "%s\n" gh pr create' # printf variant
run_case DENY 'echo done && gh pr create -t x' # later clause still judged
echo "=== Piped echo payloads (round 17): echo | bash executes the text ==="
run_case DENY 'echo "gh pr create -t x" | bash'
run_case DENY 'printf "gh pr create -t x" | sh'
run_case DENY 'echo gh pr create -t x | bash' # unquoted payload
run_case ALLOW 'echo "gh pr create -R claridtimo/x" | bash' # targeted payload
run_case ALLOW 'echo "gh pr create -t x" | grep create' # pipe into a non-shell
run_case ALLOW 'echo "gh pr create -t x" || bash' # OR, not a pipe: bash gets no stdin script
run_case ALLOW 'echo done | bash'
run_case ALLOW 'echo "gh pr create -t x" ; bash' # printed then interactive shell
echo "=== Line continuations (round 16): JSON-escaped whitespace in the degraded greps ==="
run_case DENY $'gh pr \\\ncreate -t x' # continued bare create (precise)
echo "=== Case-insensitive owner match (round 13): GitHub owners are case-insensitive ==="
run_case ALLOW 'gh pr create -R Claridtimo/bang-game -t x' # capitalized owner, targeted
run_case ALLOW 'gh pr create --repo=CLARIDTIMO/bang-game -t x' # any-case owner (precise path)
run_case DENY 'gh pr create -R Greyhavens/bang-game -t x' # case variance is not a pass
echo "=== FALLBACK route (round 13): python3 present but tokenization fails ==="
# An unbalanced quote makes shlex raise → the precise path prints FALLBACK → the degraded grep
# decides. Distinct from the no-python3 route (PATH-stripped below): this exercises the
# ValueError branch inside the python script itself.
run_case DENY 'gh pr create -t "unbalanced'
run_case ALLOW 'gh pr create -R claridtimo/x -t "unbalanced'
echo "=== Dangling value flags must not swallow a clause boundary (round 18) ==="
run_case DENY 'gh pr create -t && true -R claridtimo/x' # -t at boundary; later clause has the -R
run_case DENY 'gh pr create --title ; true -R claridtimo/x'
run_case DENY 'gh pr create -R && true' # dangling -R itself is no target
echo "=== Payload-scoping: claridtimo in cwd/paths must NOT count as a target ==="
CWD_JSON='{"cwd":"/home/dev/claridtimo/bang-game","transcript_path":"/home/dev/claridtimo/t.jsonl","tool_input":{"command":"gh pr create -t x"}}'
run_case_raw DENY "$CWD_JSON" 'bare create + claridtimo-bearing cwd (precise)'
DESC_JSON='{"tool_input":{"command":"gh pr create -t x","description":"Open PR with -R claridtimo/bang-game"},"cwd":"/x"}'
DESC_JSON2='{"tool_input":{"command":"gh pr view 63 -R claridtimo/x","description":"docs mention gh pr create"},"cwd":"/x"}'
run_case_raw DENY "$DESC_JSON" 'bare create + target only in description (precise)'
echo "=== Degraded path (no python3 on PATH) ==="
FAKEBIN="$(mktemp -d)"
trap 'rm -rf "$FAKEBIN"' EXIT
for t in bash cat grep printf env sh; do ln -sf "$(command -v $t)" "$FAKEBIN/$t"; done
run_case DENY 'gh pr create -t x' "$FAKEBIN"
run_case ALLOW 'gh pr create -R claridtimo/x -t y' "$FAKEBIN"
run_case ALLOW 'gh pr create --repo=claridtimo/x' "$FAKEBIN"
run_case ALLOW 'gh pr create -Rclaridtimo/x' "$FAKEBIN"
run_case ALLOW 'echo done' "$FAKEBIN"
run_case ALLOW 'gh pr view 63 -R claridtimo/x' "$FAKEBIN"
run_case ALLOW 'gh pr create -R Claridtimo/x -t y' "$FAKEBIN" # capitalized owner (degraded)
run_case DENY $'gh pr \\\ncreate -t x' "$FAKEBIN" # continued bare create (round 16)
run_case ALLOW $'gh pr \\\ncreate -R claridtimo/x' "$FAKEBIN" # continued targeted create
run_case ALLOW $'gh pr create -t x -R \\\nclaridtimo/x' "$FAKEBIN" # continuation inside flag adjacency
run_case_raw DENY "$CWD_JSON" 'bare create + claridtimo-bearing cwd (degraded, round 10)' "$FAKEBIN"
run_case_raw DENY "$DESC_JSON" 'bare create + target only in description (degraded, round 18)' "$FAKEBIN"
run_case_raw ALLOW "$DESC_JSON2" 'targeted view + create phrase in description (degraded, round 18)' "$FAKEBIN"
echo "=== Degraded path: ACCEPTED under-blocks, pinned (round 12) ==="
# The tokenizer-free fallback deliberately allows these two shapes: scoping flags to clauses
# with sed/grep was the v2 approach and false-denied real targeted creates (quoted PR bodies
# containing shell text). The precise path DENIES both — asserted alongside so the asymmetry is
# pinned and a future "fix" that flips either direction fails here.
run_case DENY 'gh pr view -R claridtimo/x && gh pr create -t y' # precise: real deny
run_case ALLOW 'gh pr view -R claridtimo/x && gh pr create -t y' "$FAKEBIN" # degraded: accepted
run_case ALLOW 'gh pr create --title "see -R claridtimo/x docs"' "$FAKEBIN" # degraded: accepted
echo
echo "pass=$pass fail=$fail"
[ "$fail" -eq 0 ]
+16
View File
@@ -0,0 +1,16 @@
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/enforce-pr-target.sh",
"timeout": 10
}
]
}
]
}
}
+17
View File
@@ -0,0 +1,17 @@
name: Hook battery
on:
push:
branches: [master, main]
pull_request:
jobs:
hook-battery:
name: PR-target hook battery
# The PreToolUse hook (.claude/hooks/enforce-pr-target.sh) is the enforced backstop against
# accidentally opening PRs on the upstream fork parent (fork of greyhavens/*). Verbatim copy
# of the bang-game hook (see claridtimo/bang-game#63 for the 21-round review history); the
# battery pins every bypass / false-deny class found there. Seconds-fast: bash+python3+grep.
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run the enforce-pr-target regression battery
run: .claude/hooks/test-enforce-pr-target.sh
+1
View File
@@ -0,0 +1 @@
*/target/
+19
View File
@@ -0,0 +1,19 @@
sudo: false
language: java
jdk:
- openjdk7
- oraclejdk8
branches:
only:
- master
cache:
directories:
- '$HOME/.m2/repository'
script:
- mvn -B test
- rm -rf $HOME/.m2/repository/com/threerings/vilya*
+3 -9
View File
@@ -40,17 +40,11 @@ A Maven repository containing released versions of the Vilya Java and
ActionScript artifacts are maintained here. To add a Vilya dependency to a ActionScript artifacts are maintained here. To add a Vilya dependency to a
Maven project, add the following to your `pom.xml`: Maven project, add the following to your `pom.xml`:
<repositories>
<repository>
<id>ooo-repo</id>
<url>http://threerings.github.com/maven-repo</url>
</repository>
</repositories>
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>com.threerings</groupId> <groupId>com.threerings</groupId>
<artifactId>vilya</artifactId> <artifactId>vilya</artifactId>
<version>1.3</version> <version>1.6</version>
</dependency> </dependency>
</dependencies> </dependencies>
@@ -59,8 +53,8 @@ remove the vast majority of the boilerplate above.
If you prefer to download pre-built binaries, those can be had here: If you prefer to download pre-built binaries, those can be had here:
* [vilya-1.3.jar](http://threerings.github.com/maven-repo/com/threerings/vilya/1.3/vilya-1.3.jar) * [vilya-1.6.jar](http://threerings.github.com/maven-repo/com/threerings/vilya/1.6/vilya-1.6.jar)
* [vilyalib-1.3.swc](http://threerings.github.com/maven-repo/com/threerings/vilyalib/1.3/vilyalib-1.3.swc) * [vilyalib-1.6.swc](http://threerings.github.com/maven-repo/com/threerings/vilyalib/1.6/vilyalib-a1.6.swc)
Distribution Distribution
------------ ------------
+3 -29
View File
@@ -4,7 +4,7 @@
<parent> <parent>
<groupId>com.threerings</groupId> <groupId>com.threerings</groupId>
<artifactId>vilya-parent</artifactId> <artifactId>vilya-parent</artifactId>
<version>1.5.1</version> <version>1.7-SNAPSHOT</version>
</parent> </parent>
<artifactId>vilyalib</artifactId> <artifactId>vilyalib</artifactId>
@@ -13,16 +13,8 @@
<repositories> <repositories>
<repository> <repository>
<id>flexlib-repo</id> <id>flex-mojos-repository</id>
<url>http://maven.ow2.org/maven2</url> <url>http://repository.sonatype.org/content/groups/flexgroup</url>
</repository>
<repository>
<id>ooo-ext-maven-repo</id>
<url>http://ooo-maven.googlecode.com/hg/repository</url>
</repository>
<repository>
<id>ooo-maven-repo</id>
<url>http://threerings.github.com/maven-repo</url>
</repository> </repository>
</repositories> </repositories>
@@ -31,24 +23,6 @@
</properties> </properties>
<dependencies> <dependencies>
<dependency>
<groupId>com.threerings</groupId>
<artifactId>aspirin</artifactId>
<version>1.9</version>
<type>swc</type>
</dependency>
<dependency>
<groupId>flexlib</groupId>
<artifactId>flexlib-bin</artifactId>
<version>2.4</version>
<type>swc</type>
</dependency>
<dependency>
<groupId>as3isolib</groupId>
<artifactId>as3isolib-fp9</artifactId>
<version>r298</version>
<type>swc</type>
</dependency>
<dependency> <dependency>
<groupId>com.threerings</groupId> <groupId>com.threerings</groupId>
<artifactId>nenyalib</artifactId> <artifactId>nenyalib</artifactId>
+21 -14
View File
@@ -1,9 +1,9 @@
<?xml version="1.0" standalone="yes"?> <?xml version="1.0" standalone="yes"?>
<project name="vilya" default="compile" basedir="." xmlns:artifact="urn:maven-artifact-ant"> <project name="vilya" default="compile" basedir="." xmlns:artifact="urn:maven-artifact-ant">
<property name="deploy.dir" location="dist"/> <property name="deploy.dir" location="dist"/>
<property name="src.dir" location="src/main/java"/> <property name="src.dir" location="core/src/main/java"/>
<property name="asrc.dir" location="src/main/as"/> <property name="asrc.dir" location="aslib/src/main/as"/>
<property name="tsrc.dir" location="src/test/java"/> <property name="tsrc.dir" location="core/src/test/java"/>
<property name="classes.dir" location="${deploy.dir}/classes"/> <property name="classes.dir" location="${deploy.dir}/classes"/>
<property name="tclasses.dir" location="${deploy.dir}/test-classes"/> <property name="tclasses.dir" location="${deploy.dir}/test-classes"/>
@@ -12,9 +12,14 @@
<import file="${user.home}/.m2/ooo-build/${ooo-build.vers}/ooo-build.xml"/> <import file="${user.home}/.m2/ooo-build/${ooo-build.vers}/ooo-build.xml"/>
<target name="-prepare" depends="-init-ooo"> <target name="-prepare" depends="-init-ooo">
<mavendep pom="pom.xml" id="compile"/> <mavendep pom="core/pom.xml" id="compile"/>
<mavendep pom="etc/as-pom.xml" pathProp="aslib.path"/> <!-- we can't use <mavendep> here because we need to exclude our pom depends -->
<mavendep pom="pom.xml" id="test" scope="test"/> <artifact:dependencies filesetId="aslib.fileset" cacheDependencyRefs="true" type="swc"
dependencyRefsBuildFile="${deploy.dir}/aslib-pom.dependencies">
<pom file="aslib/pom.xml"/>
</artifact:dependencies>
<pathconvert pathsep="," property="aslib.path" refid="aslib.fileset"/>
<mavendep pom="core/pom.xml" id="test" scope="test"/>
<mkdir dir="${deploy.dir}"/> <mkdir dir="${deploy.dir}"/>
<path id="built.classpath"> <path id="built.classpath">
<path refid="compile.path"/> <path refid="compile.path"/>
@@ -50,8 +55,8 @@
<delete dir="${deploy.dir}/docs"/> <delete dir="${deploy.dir}/docs"/>
<delete failonerror="false"><fileset dir="${deploy.dir}" includes="*.jar"/></delete> <delete failonerror="false"><fileset dir="${deploy.dir}" includes="*.jar"/></delete>
<delete failonerror="false"><fileset dir="${deploy.dir}" includes="*.swc"/></delete> <delete failonerror="false"><fileset dir="${deploy.dir}" includes="*.swc"/></delete>
<cleanmavendepcache pom="pom.xml"/> <cleanmavendepcache pom="core/pom.xml"/>
<cleanmavendepcache pom="etc/as-pom.xml"/> <cleanmavendepcache pom="aslib/pom.xml"/>
<cleanmavendepcache id="test"/> <cleanmavendepcache id="test"/>
</target> </target>
@@ -61,12 +66,12 @@
<target name="compile" depends="-prepare" description="Builds the Java code."> <target name="compile" depends="-prepare" description="Builds the Java code.">
<mkdir dir="${classes.dir}"/> <mkdir dir="${classes.dir}"/>
<copy todir="${classes.dir}"><fileset dir="src/main/resources" includes="**"/></copy> <copy todir="${classes.dir}"><fileset dir="core/src/main/resources" includes="**"/></copy>
<ooojavac version="1.5" srcdir="${src.dir}" destdir="${classes.dir}" <ooojavac version="1.5" srcdir="${src.dir}" destdir="${classes.dir}"
classpathref="compile.path"/> classpathref="compile.path"/>
<mkdir dir="${tclasses.dir}"/> <mkdir dir="${tclasses.dir}"/>
<copy todir="${tclasses.dir}"><fileset dir="src/test/resources" includes="**"/></copy> <copy todir="${tclasses.dir}"><fileset dir="core/src/test/resources" includes="**"/></copy>
<ooojavac srcdir="${tsrc.dir}" destdir="${tclasses.dir}" version="1.5" <ooojavac srcdir="${tsrc.dir}" destdir="${tclasses.dir}" version="1.5"
classpathref="test.classpath"/> classpathref="test.classpath"/>
</target> </target>
@@ -247,19 +252,21 @@
</target> </target>
<target name="-as-install" depends="aslib" if="flexsdk.dir"> <target name="-as-install" depends="aslib" if="flexsdk.dir">
<maveninstall file="${deploy.dir}/${ant.project.name}lib.swc" pom="etc/as-pom.xml"/> <maveninstall file="${deploy.dir}/${ant.project.name}lib.swc" pom="aslib/pom.xml"/>
</target> </target>
<target name="install" depends="dist,-as-install" <target name="install" depends="dist,-as-install"
description="Installs artifacts to local Maven repository."> description="Installs artifacts to local Maven repository.">
<maveninstall file="${deploy.dir}/${ant.project.name}.jar" pom="pom.xml"/> <maveninstall file="pom.xml" pom="pom.xml"/>
<maveninstall file="${deploy.dir}/${ant.project.name}.jar" pom="core/pom.xml"/>
</target> </target>
<target name="-as-deploy" depends="aslib" if="flexsdk.dir"> <target name="-as-deploy" depends="aslib" if="flexsdk.dir">
<mavendeploy file="${deploy.dir}/${ant.project.name}lib.swc" pom="etc/as-pom.xml"/> <mavendeploy file="${deploy.dir}/${ant.project.name}lib.swc" pom="aslib/pom.xml"/>
</target> </target>
<target name="deploy" depends="dist,-as-deploy,tests,javadoc" <target name="deploy" depends="dist,-as-deploy,tests,javadoc"
description="Deploys our build artifacts to a Maven repository."> description="Deploys our build artifacts to a Maven repository.">
<mavendeploy file="${deploy.dir}/${ant.project.name}.jar" pom="pom.xml" <mavendeploy file="pom.xml" pom="pom.xml"/>
<mavendeploy file="${deploy.dir}/${ant.project.name}.jar" pom="core/pom.xml"
srcdir="${src.dir}" docdir="${deploy.dir}/docs"/> srcdir="${src.dir}" docdir="${deploy.dir}/docs"/>
</target> </target>
</project> </project>
+9 -24
View File
@@ -4,7 +4,7 @@
<parent> <parent>
<groupId>com.threerings</groupId> <groupId>com.threerings</groupId>
<artifactId>vilya-parent</artifactId> <artifactId>vilya-parent</artifactId>
<version>1.5.1</version> <version>1.8-SNAPSHOT</version>
</parent> </parent>
<artifactId>vilya</artifactId> <artifactId>vilya</artifactId>
@@ -16,24 +16,24 @@
<dependency> <dependency>
<groupId>com.threerings</groupId> <groupId>com.threerings</groupId>
<artifactId>ooo-util</artifactId> <artifactId>ooo-util</artifactId>
<version>1.3.2</version> <version>1.6-SNAPSHOT</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.threerings</groupId> <groupId>com.threerings</groupId>
<artifactId>narya</artifactId> <artifactId>narya</artifactId>
<version>1.14</version> <version>1.20-SNAPSHOT</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.threerings</groupId> <groupId>com.threerings</groupId>
<artifactId>nenya</artifactId> <artifactId>nenya</artifactId>
<version>1.5</version> <version>1.7.3-SNAPSHOT</version>
</dependency> </dependency>
<!-- optional dependencies --> <!-- optional dependencies -->
<dependency> <dependency>
<groupId>com.threerings</groupId> <groupId>com.threerings</groupId>
<artifactId>nenya-tools</artifactId> <artifactId>nenya-tools</artifactId>
<version>1.5</version> <version>1.7.3-SNAPSHOT</version>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<dependency> <dependency>
@@ -45,13 +45,13 @@
<dependency> <dependency>
<groupId>com.samskivert</groupId> <groupId>com.samskivert</groupId>
<artifactId>depot</artifactId> <artifactId>depot</artifactId>
<version>1.6.1</version> <version>1.9-SNAPSHOT</version>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<dependency> <dependency>
<groupId>javassist</groupId> <groupId>org.javassist</groupId>
<artifactId>javassist</artifactId> <artifactId>javassist</artifactId>
<version>3.8.0.GA</version> <version>3.18.2-GA</version>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<dependency> <dependency>
@@ -72,21 +72,6 @@
<build> <build>
<plugins> <plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.7</version>
<configuration>
<quiet>true</quiet>
<show>public</show>
<links>
<link>http://samskivert.github.com/samskivert/apidocs/</link>
<link>http://depot.googlecode.com/svn/apidocs/</link>
<link>http://docs.guava-libraries.googlecode.com/git/javadoc/</link>
<link>http://google-web-toolkit.googlecode.com/svn/javadoc/2.1/</link>
</links>
</configuration>
</plugin>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId> <artifactId>maven-surefire-plugin</artifactId>
@@ -96,7 +81,7 @@
<include>com/threerings/**/*Test.java</include> <include>com/threerings/**/*Test.java</include>
</includes> </includes>
<systemPropertyVariables> <systemPropertyVariables>
<test_dir>target/test-classes</test_dir> <java.awt.headless>true</java.awt.headless>
</systemPropertyVariables> </systemPropertyVariables>
</configuration> </configuration>
</plugin> </plugin>
@@ -21,7 +21,7 @@
package com.threerings.micasa.lobby; package com.threerings.micasa.lobby;
import javax.annotation.Generated; import javax.annotation.processing.Generated;
import com.threerings.presents.data.InvocationMarshaller; import com.threerings.presents.data.InvocationMarshaller;
import java.util.List; import java.util.List;
@@ -21,7 +21,7 @@
package com.threerings.micasa.lobby; package com.threerings.micasa.lobby;
import javax.annotation.Generated; import javax.annotation.processing.Generated;
import com.threerings.presents.data.ClientObject; import com.threerings.presents.data.ClientObject;
import com.threerings.presents.server.InvocationException; import com.threerings.presents.server.InvocationException;
@@ -57,7 +57,7 @@ import static com.threerings.micasa.Log.log;
* <p> Presently, the lobby registry is configured with lobbies via the server configuration. An * <p> Presently, the lobby registry is configured with lobbies via the server configuration. An
* example configuration follows: * example configuration follows:
* *
* <pre> * <pre>{@code
* lobby_ids = foolobby, barlobby, bazlobby * lobby_ids = foolobby, barlobby, bazlobby
* *
* foolobby.mgrclass = com.threerings.micasa.lobby.LobbyManager * foolobby.mgrclass = com.threerings.micasa.lobby.LobbyManager
@@ -70,7 +70,7 @@ import static com.threerings.micasa.Log.log;
* barlobby.ugi = <universal game identifier> * barlobby.ugi = <universal game identifier>
* barlobby.name = <human readable lobby name> * barlobby.name = <human readable lobby name>
* ... * ...
* </pre> * }</pre>
* *
* This information will be loaded from the MiCasa server configuration which means that it should * This information will be loaded from the MiCasa server configuration which means that it should
* live in <code>rsrc/config/micasa/server.properties</code> somwhere in the classpath where it * live in <code>rsrc/config/micasa/server.properties</code> somwhere in the classpath where it
@@ -80,11 +80,11 @@ import static com.threerings.micasa.Log.log;
* identify every type of game and also to classify it according to meaningful keywords. It is best * identify every type of game and also to classify it according to meaningful keywords. It is best
* described with a few examples: * described with a few examples:
* *
* <pre> * <pre>{@code
* backgammon,board,strategy * backgammon,board,strategy
* spades,card,partner * spades,card,partner
* yahtzee,dice * yahtzee,dice
* </pre> * }</pre>
* *
* As you can see, a UGI should start with an identifier uniquely identifying the type of game and * As you can see, a UGI should start with an identifier uniquely identifying the type of game and
* can be followed by a list of keywords that classify it as a member of a particular category of * can be followed by a list of keywords that classify it as a member of a particular category of
@@ -21,7 +21,7 @@
package com.threerings.micasa.lobby.table; package com.threerings.micasa.lobby.table;
import javax.annotation.Generated; import javax.annotation.processing.Generated;
import com.threerings.presents.dobj.DSet; import com.threerings.presents.dobj.DSet;
import com.threerings.parlor.data.Table; import com.threerings.parlor.data.Table;
@@ -21,7 +21,7 @@
package com.threerings.micasa.simulator.data; package com.threerings.micasa.simulator.data;
import javax.annotation.Generated; import javax.annotation.processing.Generated;
import com.threerings.micasa.simulator.client.SimulatorService; import com.threerings.micasa.simulator.client.SimulatorService;
import com.threerings.parlor.game.data.GameConfig; import com.threerings.parlor.game.data.GameConfig;
@@ -21,7 +21,7 @@
package com.threerings.parlor.card.trick.data; package com.threerings.parlor.card.trick.data;
import javax.annotation.Generated; import javax.annotation.processing.Generated;
import com.threerings.parlor.card.data.Card; import com.threerings.parlor.card.data.Card;
import com.threerings.parlor.card.trick.client.TrickCardGameService; import com.threerings.parlor.card.trick.client.TrickCardGameService;
@@ -21,7 +21,7 @@
package com.threerings.parlor.card.trick.server; package com.threerings.parlor.card.trick.server;
import javax.annotation.Generated; import javax.annotation.processing.Generated;
import com.threerings.parlor.card.data.Card; import com.threerings.parlor.card.data.Card;
import com.threerings.parlor.card.trick.client.TrickCardGameService; import com.threerings.parlor.card.trick.client.TrickCardGameService;
@@ -21,7 +21,7 @@
package com.threerings.parlor.data; package com.threerings.parlor.data;
import javax.annotation.Generated; import javax.annotation.processing.Generated;
import com.threerings.parlor.client.ParlorService; import com.threerings.parlor.client.ParlorService;
import com.threerings.parlor.game.data.GameConfig; import com.threerings.parlor.game.data.GameConfig;
@@ -21,7 +21,7 @@
package com.threerings.parlor.data; package com.threerings.parlor.data;
import javax.annotation.Generated; import javax.annotation.processing.Generated;
import com.threerings.parlor.client.TableService; import com.threerings.parlor.client.TableService;
import com.threerings.parlor.game.data.GameConfig; import com.threerings.parlor.game.data.GameConfig;
@@ -21,7 +21,7 @@
package com.threerings.parlor.game.data; package com.threerings.parlor.game.data;
import javax.annotation.Generated; import javax.annotation.processing.Generated;
import com.samskivert.util.StringUtil; import com.samskivert.util.StringUtil;
import com.threerings.util.Name; import com.threerings.util.Name;
@@ -238,6 +238,11 @@ public class GameObject extends PlaceObject
return -1; return -1;
} }
@SuppressWarnings("deprecation")
public void sendPlayerReady () {
manager.invoke("playerReady");
}
/** /**
* Used by {@link #isActivePlayer} to determine if the supplied status is associated with an * Used by {@link #isActivePlayer} to determine if the supplied status is associated with an
* active player (one that has not resigned from the game and/or left the game room). * active player (one that has not resigned from the game and/or left the game room).
@@ -96,7 +96,7 @@ public class Rating
* Computes a ratings adjustment for the given player, using a modified * Computes a ratings adjustment for the given player, using a modified
* version of the FIDE Chess rating system as: * version of the FIDE Chess rating system as:
* *
* <pre> * <pre>{@code
* adjustment = K(W - We) * adjustment = K(W - We)
* *
* where: * where:
@@ -112,7 +112,7 @@ public class Rating
* We = expected score (win expectancy) as determined by: * We = expected score (win expectancy) as determined by:
* *
* We = 1 / (10^(dR/400) + 1) * We = 1 / (10^(dR/400) + 1)
* </pre> * }</pre>
* *
* @param W the win value the game in question (1.0 means the player won, * @param W the win value the game in question (1.0 means the player won,
* 0.5 means they drew, 0 means they lost). * 0.5 means they drew, 0 means they lost).
@@ -144,8 +144,8 @@ public class Rating
} }
/** /**
* Returns true if this rating is provisional (experience < 20). * Returns true if this rating is provisional ({@code experience < 20}).
*/ */
public boolean isProvisional () public boolean isProvisional ()
{ {
return (experience < 20); return (experience < 20);
@@ -213,7 +213,7 @@ public class Percentiler
{ {
if (value < _min) { if (value < _min) {
return 0; return 0;
} else if (value > _max) { } else if (value >= _max) {
return 100; return 100;
} else { } else {
return _percentile[toBucketIndex(value)]; return _percentile[toBucketIndex(value)];
@@ -21,7 +21,7 @@
package com.threerings.parlor.server; package com.threerings.parlor.server;
import javax.annotation.Generated; import javax.annotation.processing.Generated;
import com.threerings.parlor.client.ParlorService; import com.threerings.parlor.client.ParlorService;
import com.threerings.parlor.game.data.GameConfig; import com.threerings.parlor.game.data.GameConfig;
@@ -21,7 +21,7 @@
package com.threerings.parlor.server; package com.threerings.parlor.server;
import javax.annotation.Generated; import javax.annotation.processing.Generated;
import com.threerings.parlor.client.TableService; import com.threerings.parlor.client.TableService;
import com.threerings.parlor.data.TableConfig; import com.threerings.parlor.data.TableConfig;
@@ -21,7 +21,7 @@
package com.threerings.parlor.tourney.data; package com.threerings.parlor.tourney.data;
import javax.annotation.Generated; import javax.annotation.processing.Generated;
import com.threerings.parlor.tourney.client.TourneyService; import com.threerings.parlor.tourney.client.TourneyService;
import com.threerings.presents.client.InvocationService; import com.threerings.presents.client.InvocationService;
@@ -21,7 +21,7 @@
package com.threerings.parlor.tourney.data; package com.threerings.parlor.tourney.data;
import javax.annotation.Generated; import javax.annotation.processing.Generated;
import com.threerings.presents.dobj.DObject; import com.threerings.presents.dobj.DObject;
import com.threerings.presents.dobj.DSet; import com.threerings.presents.dobj.DSet;
@@ -21,7 +21,7 @@
package com.threerings.parlor.tourney.data; package com.threerings.parlor.tourney.data;
import javax.annotation.Generated; import javax.annotation.processing.Generated;
import com.threerings.parlor.tourney.client.TourniesService; import com.threerings.parlor.tourney.client.TourniesService;
import com.threerings.presents.client.InvocationService; import com.threerings.presents.client.InvocationService;
@@ -21,7 +21,7 @@
package com.threerings.parlor.tourney.server; package com.threerings.parlor.tourney.server;
import javax.annotation.Generated; import javax.annotation.processing.Generated;
import com.threerings.parlor.tourney.client.TourneyService; import com.threerings.parlor.tourney.client.TourneyService;
import com.threerings.presents.client.InvocationService; import com.threerings.presents.client.InvocationService;
@@ -21,7 +21,7 @@
package com.threerings.parlor.tourney.server; package com.threerings.parlor.tourney.server;
import javax.annotation.Generated; import javax.annotation.processing.Generated;
import com.threerings.parlor.tourney.client.TourniesService; import com.threerings.parlor.tourney.client.TourniesService;
import com.threerings.parlor.tourney.data.TourneyConfig; import com.threerings.parlor.tourney.data.TourneyConfig;
@@ -21,7 +21,7 @@
package com.threerings.puzzle.data; package com.threerings.puzzle.data;
import javax.annotation.Generated; import javax.annotation.processing.Generated;
import com.threerings.presents.data.InvocationMarshaller; import com.threerings.presents.data.InvocationMarshaller;
import com.threerings.puzzle.client.PuzzleGameService; import com.threerings.puzzle.client.PuzzleGameService;
@@ -21,7 +21,7 @@
package com.threerings.puzzle.data; package com.threerings.puzzle.data;
import javax.annotation.Generated; import javax.annotation.processing.Generated;
import com.threerings.parlor.game.data.GameObject; import com.threerings.parlor.game.data.GameObject;
/** /**
@@ -21,7 +21,7 @@
package com.threerings.puzzle.server; package com.threerings.puzzle.server;
import javax.annotation.Generated; import javax.annotation.processing.Generated;
import com.threerings.presents.data.ClientObject; import com.threerings.presents.data.ClientObject;
import com.threerings.presents.server.InvocationProvider; import com.threerings.presents.server.InvocationProvider;
@@ -21,7 +21,7 @@
package com.threerings.stage.data; package com.threerings.stage.data;
import javax.annotation.Generated; import javax.annotation.processing.Generated;
import com.threerings.miso.data.ObjectInfo; import com.threerings.miso.data.ObjectInfo;
import com.threerings.presents.client.InvocationService; import com.threerings.presents.client.InvocationService;
@@ -21,7 +21,7 @@
package com.threerings.stage.data; package com.threerings.stage.data;
import javax.annotation.Generated; import javax.annotation.processing.Generated;
import com.threerings.whirled.spot.data.SpotSceneObject; import com.threerings.whirled.spot.data.SpotSceneObject;
/** /**
@@ -21,7 +21,7 @@
package com.threerings.stage.server; package com.threerings.stage.server;
import javax.annotation.Generated; import javax.annotation.processing.Generated;
import com.threerings.miso.data.ObjectInfo; import com.threerings.miso.data.ObjectInfo;
import com.threerings.presents.client.InvocationService; import com.threerings.presents.client.InvocationService;
@@ -76,7 +76,7 @@ public class TestTileLoader implements TileSetIDBroker
* will be used to create tilesets for all the .png files in the same * will be used to create tilesets for all the .png files in the same
* directory. * directory.
* *
* @return a HashIntMap containing a TileSetId -> TileSet mapping for * @return a HashIntMap containing a {@code TileSetId -> TileSet} mapping for
* all the tilesets we create. * all the tilesets we create.
*/ */
public HashIntMap<TileSet> loadTestTiles () public HashIntMap<TileSet> loadTestTiles ()
@@ -46,7 +46,7 @@ public class IntSetStat extends SetStat<Integer>
/** /**
* Constructs a new IntSetStat that will store up to maxSize ints. * Constructs a new IntSetStat that will store up to maxSize ints.
* *
* @param maxSize the maximum number of ints to store in the IntSetStat. Must be <= 255. * @param maxSize the maximum number of ints to store in the IntSetStat. Must be {@code <= 255}.
*/ */
public IntSetStat (int maxSize) public IntSetStat (int maxSize)
{ {
@@ -21,7 +21,7 @@
package com.threerings.whirled.data; package com.threerings.whirled.data;
import javax.annotation.Generated; import javax.annotation.processing.Generated;
import com.threerings.crowd.data.PlaceConfig; import com.threerings.crowd.data.PlaceConfig;
import com.threerings.presents.data.InvocationMarshaller; import com.threerings.presents.data.InvocationMarshaller;
@@ -21,7 +21,7 @@
package com.threerings.whirled.server; package com.threerings.whirled.server;
import javax.annotation.Generated; import javax.annotation.processing.Generated;
import com.threerings.presents.data.ClientObject; import com.threerings.presents.data.ClientObject;
import com.threerings.presents.server.InvocationException; import com.threerings.presents.server.InvocationException;
@@ -22,9 +22,12 @@
package com.threerings.whirled.spot.data; package com.threerings.whirled.spot.data;
import java.awt.Rectangle; import java.awt.Rectangle;
import java.io.IOException;
import com.samskivert.util.StringUtil; import com.samskivert.util.StringUtil;
import com.threerings.io.ObjectInputStream;
import com.threerings.io.ObjectOutputStream;
import com.threerings.util.ActionScript; import com.threerings.util.ActionScript;
import com.threerings.presents.dobj.DSet; import com.threerings.presents.dobj.DSet;
@@ -48,6 +51,31 @@ public class Cluster extends Rectangle
return _key; return _key;
} }
/**
* Writes our streamable fields. This is needed because we can no longer reflectively access the
* internals of java.awt.Rectangle.
*/
public void writeObject (ObjectOutputStream out)
throws IOException
{
out.writeInt(this.x);
out.writeInt(this.y);
out.writeInt(this.width);
out.writeInt(this.height);
out.writeInt(this.clusterOid);
}
/**
* Reads our streamable fields. This is needed because we can no longer reflectively access the
* internals of java.awt.Rectangle.
*/
public void readObject (ObjectInputStream in)
throws IOException, ClassNotFoundException
{
this.setBounds(in.readInt(), in.readInt(), in.readInt(), in.readInt());
this.clusterOid = in.readInt();
}
@Override @Override
@ActionScript(omit=true) @ActionScript(omit=true)
public boolean equals (Object other) public boolean equals (Object other)
@@ -21,11 +21,12 @@
package com.threerings.whirled.spot.data; package com.threerings.whirled.spot.data;
import javax.annotation.Generated; import javax.annotation.processing.Generated;
import com.threerings.presents.dobj.DObject; import com.threerings.presents.dobj.DObject;
import com.threerings.presents.dobj.OidList; import com.threerings.presents.dobj.OidList;
import com.threerings.crowd.chat.data.SpeakObject; import com.threerings.crowd.chat.data.SpeakObject;
import com.threerings.crowd.chat.data.UserMessage;
/** /**
* Used to dispatch chat in clusters. * Used to dispatch chat in clusters.
@@ -48,10 +49,16 @@ public class ClusterObject extends DObject
public void applyToListeners (ListenerOp op) public void applyToListeners (ListenerOp op)
{ {
for (int ii = 0, ll = occupants.size(); ii < ll; ii++) { for (int ii = 0, ll = occupants.size(); ii < ll; ii++) {
op.apply(occupants.get(ii)); op.apply(this, occupants.get(ii));
} }
} }
// from SpeakObject
public String getChatIdentifier (UserMessage msg)
{
return SpeakObject.DEFAULT_IDENTIFIER;
}
// AUTO-GENERATED: METHODS START // AUTO-GENERATED: METHODS START
/** /**
* Requests that <code>oid</code> be added to the <code>occupants</code> * Requests that <code>oid</code> be added to the <code>occupants</code>
@@ -21,7 +21,7 @@
package com.threerings.whirled.spot.data; package com.threerings.whirled.spot.data;
import javax.annotation.Generated; import javax.annotation.processing.Generated;
import com.threerings.crowd.data.PlaceConfig; import com.threerings.crowd.data.PlaceConfig;
import com.threerings.presents.client.InvocationService; import com.threerings.presents.client.InvocationService;
@@ -21,7 +21,7 @@
package com.threerings.whirled.spot.data; package com.threerings.whirled.spot.data;
import javax.annotation.Generated; import javax.annotation.processing.Generated;
import com.threerings.presents.dobj.DSet; import com.threerings.presents.dobj.DSet;
import com.threerings.whirled.data.SceneObject; import com.threerings.whirled.data.SceneObject;
@@ -95,9 +95,9 @@
providing top-down 2D display of its scenes. <br> providing top-down 2D display of its scenes. <br>
</p> </p>
<center> <p>
<img align="center" src="images/scene.png" alt="Example scene image"> <img src="images/scene.png" alt="Example scene image">
</center> </p>
<p> The arrows would, of course, not be rendered but are shown to <p> The arrows would, of course, not be rendered but are shown to
indicate the presence of locations (and portals) and their indicate the presence of locations (and portals) and their
@@ -21,7 +21,7 @@
package com.threerings.whirled.spot.server; package com.threerings.whirled.spot.server;
import javax.annotation.Generated; import javax.annotation.processing.Generated;
import com.threerings.presents.client.InvocationService; import com.threerings.presents.client.InvocationService;
import com.threerings.presents.data.ClientObject; import com.threerings.presents.data.ClientObject;
@@ -23,6 +23,7 @@ package com.threerings.whirled.tools.xml;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import org.xml.sax.SAXException; import org.xml.sax.SAXException;
@@ -90,15 +91,23 @@ public class SceneParser
} }
/** /**
* Parses the XML file at the specified path into a scene model * Parses the XML file at the specified path into a scene model instance.
* instance.
*/ */
public SceneModel parseScene (String path) public SceneModel parseScene (String path)
throws IOException, SAXException throws IOException, SAXException
{
return parseScene(new FileInputStream(path));
}
/**
* Parses the XML file provided by {@code scene} into a scene model instance.
*/
public SceneModel parseScene (InputStream scene)
throws IOException, SAXException
{ {
_model = null; _model = null;
_digester.push(this); _digester.push(this);
_digester.parse(new FileInputStream(path)); _digester.parse(scene);
return _model; return _model;
} }
@@ -21,7 +21,7 @@
package com.threerings.whirled.zone.data; package com.threerings.whirled.zone.data;
import javax.annotation.Generated; import javax.annotation.processing.Generated;
import com.threerings.crowd.data.PlaceConfig; import com.threerings.crowd.data.PlaceConfig;
import com.threerings.presents.data.InvocationMarshaller; import com.threerings.presents.data.InvocationMarshaller;
@@ -21,7 +21,7 @@
package com.threerings.whirled.zone.peer.server; package com.threerings.whirled.zone.peer.server;
import javax.annotation.Generated; import javax.annotation.processing.Generated;
import com.threerings.presents.dobj.DSet; import com.threerings.presents.dobj.DSet;
import com.threerings.crowd.peer.data.CrowdNodeObject; import com.threerings.crowd.peer.data.CrowdNodeObject;
@@ -21,7 +21,7 @@
package com.threerings.whirled.zone.server; package com.threerings.whirled.zone.server;
import javax.annotation.Generated; import javax.annotation.processing.Generated;
import com.threerings.presents.data.ClientObject; import com.threerings.presents.data.ClientObject;
import com.threerings.presents.server.InvocationException; import com.threerings.presents.server.InvocationException;
@@ -24,8 +24,6 @@ package com.threerings.whirled.spot.tools.xml;
import junit.framework.Test; import junit.framework.Test;
import junit.framework.TestCase; import junit.framework.TestCase;
import com.samskivert.test.TestUtil;
import com.threerings.whirled.data.SceneModel; import com.threerings.whirled.data.SceneModel;
import com.threerings.whirled.spot.data.Location; import com.threerings.whirled.spot.data.Location;
import com.threerings.whirled.tools.xml.SceneParser; import com.threerings.whirled.tools.xml.SceneParser;
@@ -50,8 +48,8 @@ public class SpotSceneParserTest extends TestCase
return new StageLocation(); // breaks package, but ok return new StageLocation(); // breaks package, but ok
} }
}); });
String tspath = TestUtil.getResourcePath(TEST_SCENE_PATH); SceneModel scene = parser.parseScene(
SceneModel scene = parser.parseScene(tspath); getClass().getClassLoader().getResourceAsStream(TEST_SCENE_PATH));
System.out.println("Parsed " + scene + "."); System.out.println("Parsed " + scene + ".");
} catch (Exception e) { } catch (Exception e) {
@@ -24,8 +24,6 @@ package com.threerings.whirled.tools.xml;
import junit.framework.Test; import junit.framework.Test;
import junit.framework.TestCase; import junit.framework.TestCase;
import com.samskivert.test.TestUtil;
import com.threerings.whirled.data.SceneModel; import com.threerings.whirled.data.SceneModel;
public class SceneParserTest extends TestCase public class SceneParserTest extends TestCase
@@ -40,8 +38,8 @@ public class SceneParserTest extends TestCase
{ {
try { try {
SceneParser parser = new SceneParser("scene"); SceneParser parser = new SceneParser("scene");
String tspath = TestUtil.getResourcePath(TEST_SCENE_PATH); SceneModel scene = parser.parseScene(
SceneModel scene = parser.parseScene(tspath); getClass().getClassLoader().getResourceAsStream(TEST_SCENE_PATH));
System.out.println("Parsed " + scene + "."); System.out.println("Parsed " + scene + ".");
} catch (Exception e) { } catch (Exception e) {
-47
View File
@@ -1,47 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<groupId>com.threerings</groupId>
<artifactId>vilyalib</artifactId>
<packaging>swc</packaging>
<version>1.5-SNAPSHOT</version>
<name>Vilya for ActionScript</name>
<description>Facilities for making networked multiplayer games.</description>
<url>http://code.google.com/p/vilya/</url>
<issueManagement>
<url>http://code.google.com/p/vilya/issues/list</url>
</issueManagement>
<repositories>
<repository>
<id>flexlib-repo</id>
<url>http://maven.ow2.org/maven2</url>
</repository>
<repository>
<id>ooo-maven-repo</id>
<url>http://ooo-maven.googlecode.com/hg/repository</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>com.threerings</groupId>
<artifactId>naryalib</artifactId>
<version>1.12.7</version>
<type>swc</type>
</dependency>
<dependency>
<groupId>com.threerings</groupId>
<artifactId>nenyalib</artifactId>
<version>1.4.2</version>
<type>swc</type>
</dependency>
<dependency>
<groupId>as3isolib</groupId>
<artifactId>as3isolib-fp9</artifactId>
<version>r298</version>
<type>swc</type>
</dependency>
</dependencies>
</project>
+131 -57
View File
@@ -1,28 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.sonatype.oss</groupId>
<artifactId>oss-parent</artifactId>
<version>7</version>
</parent>
<groupId>com.threerings</groupId> <groupId>com.threerings</groupId>
<artifactId>vilya-parent</artifactId> <artifactId>vilya-parent</artifactId>
<packaging>pom</packaging> <packaging>pom</packaging>
<version>1.5.1</version> <version>1.8-SNAPSHOT</version>
<name>Vilya</name> <name>Vilya</name>
<description>Facilities for making networked multiplayer games.</description> <description>Facilities for making networked multiplayer games.</description>
<url>http://github.com/threerings/vilya/</url> <url>https://github.com/threerings/vilya/</url>
<issueManagement> <issueManagement>
<url>http://github.com/threerings/vilya/issues</url> <url>https://github.com/threerings/vilya/issues</url>
</issueManagement> </issueManagement>
<licenses> <licenses>
<license> <license>
<name>GNU Lesser General Public License (LGPL), Version 2.1</name> <name>GNU Lesser General Public License (LGPL), Version 2.1</name>
<url>http://www.fsf.org/licensing/licenses/lgpl.txt</url> <url>https://www.fsf.org/licensing/licenses/lgpl.txt</url>
<distribution>repo</distribution> <distribution>repo</distribution>
</license> </license>
</licenses> </licenses>
@@ -38,36 +33,34 @@
<scm> <scm>
<connection>scm:git:git://github.com/threerings/vilya.git</connection> <connection>scm:git:git://github.com/threerings/vilya.git</connection>
<developerConnection>scm:git:git@github.com:threerings/vilya.git</developerConnection> <developerConnection>scm:git:git@github.com:threerings/vilya.git</developerConnection>
<url>http://github.com/threerings/vilya/</url> <url>https://github.com/threerings/vilya/</url>
<tag>HEAD</tag>
</scm> </scm>
<repositories>
<repository>
<id>ooo-repo</id>
<url>http://threerings.github.com/maven-repo</url>
</repository>
</repositories>
<modules> <modules>
<module>core</module> <module>core</module>
<!--<module>tools</module>-->
<module>aslib</module>
</modules> </modules>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<build> <build>
<plugins> <plugins>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId> <artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version> <version>3.13.0</version>
<configuration> <configuration>
<source>1.5</source> <release>10</release>
<target>1.5</target>
<fork>true</fork> <fork>true</fork>
<showDeprecation>true</showDeprecation> <showDeprecation>true</showDeprecation>
<showWarnings>true</showWarnings> <showWarnings>true</showWarnings>
<!-- yes, those quoted spaces are a workaround sanctioned by the Maven idiocracy --> <compilerArgs>
<compilerArgument>-Xlint" "-Xlint:-serial" "-Xlint:-path</compilerArgument> <arg>-Xlint</arg>
<arg>-Xlint:-serial</arg>
<arg>-Xlint:-path</arg>
</compilerArgs>
</configuration> </configuration>
</plugin> </plugin>
<plugin> <plugin>
@@ -83,6 +76,16 @@
<artifactId>maven-jar-plugin</artifactId> <artifactId>maven-jar-plugin</artifactId>
<version>2.3.1</version> <version>2.3.1</version>
</plugin> </plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.9.1</version>
<configuration>
<quiet>true</quiet>
<show>public</show>
<additionalparam>-Xdoclint:all -Xdoclint:-missing</additionalparam>
</configuration>
</plugin>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-install-plugin</artifactId> <artifactId>maven-install-plugin</artifactId>
@@ -93,52 +96,105 @@
<artifactId>maven-deploy-plugin</artifactId> <artifactId>maven-deploy-plugin</artifactId>
<version>2.5</version> <version>2.5</version>
</plugin> </plugin>
<plugin>
<groupId>org.sonatype.central</groupId>
<artifactId>central-publishing-maven-plugin</artifactId>
<version>0.8.0</version>
<extensions>true</extensions>
<configuration>
<publishingServerId>central</publishingServerId>
<autoPublish>true</autoPublish>
</configuration>
</plugin>
</plugins> </plugins>
<pluginManagement>
<plugins>
<plugin>
<!-- Tell m2eclipse to ignore the enforcer plugin from our parent. Otherwise it warns
about not being able to run it. -->
<groupId>org.eclipse.m2e</groupId>
<artifactId>lifecycle-mapping</artifactId>
<version>1.0.0</version>
<configuration>
<lifecycleMappingMetadata>
<pluginExecutions>
<pluginExecution>
<pluginExecutionFilter>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<versionRange>[1.0,)</versionRange>
<goals>
<goal>enforce</goal>
</goals>
</pluginExecutionFilter>
<action>
<ignore />
</action>
</pluginExecution>
</pluginExecutions>
</lifecycleMappingMetadata>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build> </build>
<profiles> <profiles>
<!-- build with mvn -Paslib to build the ActionScript code; the Maven ActionScript plugins
are old and broken, so I've disabled this by default -->
<profile> <profile>
<id>release-sign-artifacts</id> <id>aslib</id>
<modules><module>aslib</module></modules>
</profile>
<profile>
<id>eclipse</id>
<activation>
<property>
<name>m2e.version</name>
</property>
</activation>
<build>
<pluginManagement>
<plugins>
<plugin>
<!-- Tell m2eclipse to ignore the enforcer plugin from our parent. Otherwise it warns
about not being able to run it. -->
<groupId>org.eclipse.m2e</groupId>
<artifactId>lifecycle-mapping</artifactId>
<version>1.0.0</version>
<configuration>
<lifecycleMappingMetadata>
<pluginExecutions>
<pluginExecution>
<pluginExecutionFilter>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<versionRange>[1.0,)</versionRange>
<goals>
<goal>enforce</goal>
</goals>
</pluginExecutionFilter>
<action>
<ignore />
</action>
</pluginExecution>
</pluginExecutions>
</lifecycleMappingMetadata>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
</profile>
<profile>
<id>release-extras</id>
<activation> <activation>
<property><name>performRelease</name><value>true</value></property> <property><name>performRelease</name><value>true</value></property>
</activation> </activation>
<build> <build>
<plugins> <plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>2.1.2</version>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.9.1</version>
<executions>
<execution>
<id>attach-javadocs</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId> <artifactId>maven-gpg-plugin</artifactId>
<version>1.1</version> <version>1.5</version>
<executions> <executions>
<execution> <execution>
<id>sign-artifacts</id> <id>sign-artifacts</id>
@@ -155,5 +211,23 @@
</plugins> </plugins>
</build> </build>
</profile> </profile>
<!-- For deploying to a local place -->
<profile>
<id>deploy-local</id>
<distributionManagement>
<repository>
<id>deploy-local-dir</id>
<name>Local Maven repository</name>
<url>file://${deploy.local.dir}</url>
</repository>
<snapshotRepository>
<id>deploy-local-dir</id>
<name>Local Maven repository</name>
<url>file://${deploy.local.dir}</url>
</snapshotRepository>
</distributionManagement>
</profile>
</profiles> </profiles>
</project> </project>