Sicherung
This commit is contained in:
parent
d979c9ee73
commit
280c620a15
@ -20,8 +20,8 @@ focus_threshold=0.02
|
||||
# TourEngine internal update throttle.
|
||||
# 0.033 ~= 30 Hz camera updates while still computing the path at move_step.
|
||||
tour_cam_min_interval=0.033
|
||||
tour_pos_epsilon=0.005
|
||||
tour_focus_epsilon=0.005
|
||||
tour_pos_epsilon=0.002
|
||||
tour_focus_epsilon=0.002
|
||||
|
||||
# Follow smoothing aid
|
||||
follow_predict=0.10
|
||||
|
||||
1642
HS_CamEngineCore.lsl
1642
HS_CamEngineCore.lsl
File diff suppressed because it is too large
Load Diff
1845
HS_CamEngineTour.lsl
1845
HS_CamEngineTour.lsl
File diff suppressed because it is too large
Load Diff
1010
HS_CamFov.lsl
1010
HS_CamFov.lsl
File diff suppressed because it is too large
Load Diff
1001
HS_CamMarkers.lsl
1001
HS_CamMarkers.lsl
File diff suppressed because it is too large
Load Diff
1738
HS_CamMenu.lsl
1738
HS_CamMenu.lsl
File diff suppressed because it is too large
Load Diff
1816
HS_CamPlaylist.lsl
1816
HS_CamPlaylist.lsl
File diff suppressed because it is too large
Load Diff
329
HS_CamPresetTransfer.lsl
Normal file
329
HS_CamPresetTransfer.lsl
Normal file
@ -0,0 +1,329 @@
|
||||
/*
|
||||
HS_DollyCam - Preset Transfer Utility
|
||||
|
||||
Drop this script into a camera object only when importing/exporting presets.
|
||||
It reads and writes the existing Linkset Data preset keys: P1, P2, ...
|
||||
|
||||
Commands on channel 88:
|
||||
/88 xfer help
|
||||
/88 xfer status
|
||||
/88 xfer export [from] [count]
|
||||
/88 xfer import <notecard>
|
||||
/88 xfer importreplace <notecard>
|
||||
/88 xfer poof
|
||||
|
||||
Export format:
|
||||
HS_DOLLYCAM_PRESETS_V1
|
||||
P1=px|py|pz|fx|fy|fz|rx|ry|rz|rs|fovRad
|
||||
HS_DOLLYCAM_PRESETS_END
|
||||
*/
|
||||
|
||||
integer CH = 88;
|
||||
integer MAX_PRESET_SCAN = 999;
|
||||
|
||||
string HEADER = "HS_DOLLYCAM_PRESETS_V1";
|
||||
string FOOTER = "HS_DOLLYCAM_PRESETS_END";
|
||||
|
||||
key gOwner;
|
||||
integer gListen;
|
||||
|
||||
string gImportCard;
|
||||
key gImportQuery;
|
||||
integer gImportLine;
|
||||
integer gImporting;
|
||||
integer gReplaceBeforeImport;
|
||||
integer gImported;
|
||||
integer gSkipped;
|
||||
|
||||
string preKey(integer idx)
|
||||
{
|
||||
return "P" + (string)idx;
|
||||
}
|
||||
|
||||
say(string msg)
|
||||
{
|
||||
llOwnerSay(msg);
|
||||
}
|
||||
|
||||
string firstToken(string msg)
|
||||
{
|
||||
msg = llStringTrim(msg, STRING_TRIM);
|
||||
integer sp = llSubStringIndex(msg, " ");
|
||||
if (sp < 0) return msg;
|
||||
return llGetSubString(msg, 0, sp - 1);
|
||||
}
|
||||
|
||||
string restAfterFirstToken(string msg)
|
||||
{
|
||||
msg = llStringTrim(msg, STRING_TRIM);
|
||||
integer sp = llSubStringIndex(msg, " ");
|
||||
if (sp < 0) return "";
|
||||
return llStringTrim(llGetSubString(msg, sp + 1, -1), STRING_TRIM);
|
||||
}
|
||||
|
||||
integer isCommentOrMarker(string line)
|
||||
{
|
||||
if (line == "") return TRUE;
|
||||
if (line == HEADER) return TRUE;
|
||||
if (line == FOOTER) return TRUE;
|
||||
if (llGetSubString(line, 0, 0) == "#") return TRUE;
|
||||
if (llGetSubString(line, 0, 1) == "//") return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
integer hasPresetMinimumFields(string data)
|
||||
{
|
||||
integer pipes = 0;
|
||||
integer pos = -1;
|
||||
integer len = llStringLength(data);
|
||||
|
||||
while (pipes < 5) {
|
||||
integer next = llSubStringIndex(llGetSubString(data, pos + 1, -1), "|");
|
||||
if (next < 0) return FALSE;
|
||||
pos += next + 1;
|
||||
++pipes;
|
||||
if (pos >= len) return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
integer validPresetIndexString(string s)
|
||||
{
|
||||
integer len = llStringLength(s);
|
||||
if (len < 2) return FALSE;
|
||||
if (llToUpper(llGetSubString(s, 0, 0)) != "P") return FALSE;
|
||||
|
||||
integer idx = (integer)llGetSubString(s, 1, -1);
|
||||
if (idx < 1 || idx > MAX_PRESET_SCAN) return FALSE;
|
||||
|
||||
if ("P" + (string)idx != llToUpper(s)) return FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
integer importLine(string line)
|
||||
{
|
||||
line = llStringTrim(line, STRING_TRIM);
|
||||
if (isCommentOrMarker(line)) return FALSE;
|
||||
|
||||
integer eq = llSubStringIndex(line, "=");
|
||||
if (eq < 2) return FALSE;
|
||||
|
||||
string keyName = llStringTrim(llGetSubString(line, 0, eq - 1), STRING_TRIM);
|
||||
string data = llStringTrim(llGetSubString(line, eq + 1, -1), STRING_TRIM);
|
||||
|
||||
if (!validPresetIndexString(keyName)) return FALSE;
|
||||
if (!hasPresetMinimumFields(data)) return FALSE;
|
||||
|
||||
integer idx = (integer)llGetSubString(keyName, 1, -1);
|
||||
llLinksetDataWrite(preKey(idx), data);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
clearPresets()
|
||||
{
|
||||
integer i;
|
||||
for (i = 1; i <= MAX_PRESET_SCAN; ++i) {
|
||||
llLinksetDataDelete(preKey(i));
|
||||
}
|
||||
}
|
||||
|
||||
printHelp()
|
||||
{
|
||||
say(
|
||||
"HS DollyCam Preset Transfer\n"
|
||||
+ "/88 xfer status\n"
|
||||
+ "/88 xfer export [from] [count]\n"
|
||||
+ "/88 xfer import <notecard>\n"
|
||||
+ "/88 xfer importreplace <notecard>\n"
|
||||
+ "/88 xfer poof\n"
|
||||
+ "Import notecard lines use: P1=px|py|pz|fx|fy|fz|rx|ry|rz|rs|fovRad"
|
||||
);
|
||||
}
|
||||
|
||||
printStatus()
|
||||
{
|
||||
integer count = 0;
|
||||
integer first = 0;
|
||||
integer last = 0;
|
||||
integer i;
|
||||
|
||||
for (i = 1; i <= MAX_PRESET_SCAN; ++i) {
|
||||
if (llLinksetDataRead(preKey(i)) != "") {
|
||||
++count;
|
||||
if (first == 0) first = i;
|
||||
last = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (count == 0) {
|
||||
say("Preset Transfer: no saved presets found.");
|
||||
return;
|
||||
}
|
||||
say("Preset Transfer: " + (string)count + " presets found, first P" + (string)first + ", last P" + (string)last + ".");
|
||||
}
|
||||
|
||||
exportPresets(string args)
|
||||
{
|
||||
integer from = 1;
|
||||
integer count = 50;
|
||||
|
||||
string tok = firstToken(args);
|
||||
if (tok != "") {
|
||||
from = (integer)tok;
|
||||
string rest = restAfterFirstToken(args);
|
||||
tok = firstToken(rest);
|
||||
if (tok != "") count = (integer)tok;
|
||||
}
|
||||
|
||||
if (from < 1) from = 1;
|
||||
if (from > MAX_PRESET_SCAN) from = MAX_PRESET_SCAN;
|
||||
if (count < 1) count = 50;
|
||||
if (count > 100) count = 100;
|
||||
|
||||
integer stop = from + count - 1;
|
||||
if (stop > MAX_PRESET_SCAN) stop = MAX_PRESET_SCAN;
|
||||
|
||||
integer shown = 0;
|
||||
integer i;
|
||||
|
||||
say(HEADER);
|
||||
for (i = from; i <= stop; ++i) {
|
||||
string data = llLinksetDataRead(preKey(i));
|
||||
if (data != "") {
|
||||
say(preKey(i) + "=" + data);
|
||||
++shown;
|
||||
}
|
||||
}
|
||||
say(FOOTER);
|
||||
|
||||
if (shown == 0) {
|
||||
say("Preset Transfer: no presets found in requested range.");
|
||||
return;
|
||||
}
|
||||
say("Preset Transfer: exported " + (string)shown + " presets from P" + (string)from + " to P" + (string)stop + ".");
|
||||
}
|
||||
|
||||
startImport(string card, integer replace)
|
||||
{
|
||||
card = llStringTrim(card, STRING_TRIM);
|
||||
if (card == "") {
|
||||
say("Preset Transfer: missing notecard name.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (llGetInventoryType(card) != INVENTORY_NOTECARD) {
|
||||
say("Preset Transfer: notecard not found: " + card);
|
||||
return;
|
||||
}
|
||||
|
||||
if (gImporting) {
|
||||
say("Preset Transfer: import already running.");
|
||||
return;
|
||||
}
|
||||
|
||||
gImportCard = card;
|
||||
gImportLine = 0;
|
||||
gImported = 0;
|
||||
gSkipped = 0;
|
||||
gReplaceBeforeImport = replace;
|
||||
gImporting = TRUE;
|
||||
|
||||
if (gReplaceBeforeImport) {
|
||||
clearPresets();
|
||||
say("Preset Transfer: cleared existing P1-P" + (string)MAX_PRESET_SCAN + " presets.");
|
||||
}
|
||||
|
||||
say("Preset Transfer: importing from notecard '" + gImportCard + "' ...");
|
||||
gImportQuery = llGetNotecardLine(gImportCard, gImportLine);
|
||||
}
|
||||
|
||||
handleCommand(string msg)
|
||||
{
|
||||
string cmd = llToLower(firstToken(msg));
|
||||
if (cmd != "xfer" && cmd != "transfer") return;
|
||||
|
||||
string rest = restAfterFirstToken(msg);
|
||||
string sub = llToLower(firstToken(rest));
|
||||
string args = restAfterFirstToken(rest);
|
||||
|
||||
if (sub == "" || sub == "help") {
|
||||
printHelp();
|
||||
return;
|
||||
}
|
||||
|
||||
if (sub == "status") {
|
||||
printStatus();
|
||||
return;
|
||||
}
|
||||
|
||||
if (sub == "export") {
|
||||
exportPresets(args);
|
||||
return;
|
||||
}
|
||||
|
||||
if (sub == "import") {
|
||||
startImport(args, FALSE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (sub == "importreplace") {
|
||||
startImport(args, TRUE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (sub == "poof" || sub == "done" || sub == "remove") {
|
||||
say("Preset Transfer: removing script.");
|
||||
llRemoveInventory(llGetScriptName());
|
||||
return;
|
||||
}
|
||||
|
||||
say("Preset Transfer: unknown command. Use /88 xfer help");
|
||||
}
|
||||
|
||||
default
|
||||
{
|
||||
state_entry()
|
||||
{
|
||||
gOwner = llGetOwner();
|
||||
gListen = llListen(CH, "", "", "");
|
||||
say("HS DollyCam Preset Transfer ready. Use /88 xfer help");
|
||||
}
|
||||
|
||||
on_rez(integer start_param)
|
||||
{
|
||||
gOwner = llGetOwner();
|
||||
}
|
||||
|
||||
changed(integer change)
|
||||
{
|
||||
if (change & CHANGED_OWNER) {
|
||||
llResetScript();
|
||||
}
|
||||
}
|
||||
|
||||
listen(integer channel, string name, key id, string msg)
|
||||
{
|
||||
if (llGetOwnerKey(id) != gOwner) return;
|
||||
handleCommand(llStringTrim(msg, STRING_TRIM));
|
||||
}
|
||||
|
||||
dataserver(key query_id, string data)
|
||||
{
|
||||
if (!gImporting || query_id != gImportQuery) return;
|
||||
|
||||
if (data == EOF) {
|
||||
say("Preset Transfer: import complete. imported=" + (string)gImported + " skipped=" + (string)gSkipped + ".");
|
||||
gImporting = FALSE;
|
||||
return;
|
||||
}
|
||||
|
||||
if (importLine(data)) ++gImported;
|
||||
else {
|
||||
string trimmed = llStringTrim(data, STRING_TRIM);
|
||||
if (!isCommentOrMarker(trimmed)) ++gSkipped;
|
||||
}
|
||||
|
||||
++gImportLine;
|
||||
gImportQuery = llGetNotecardLine(gImportCard, gImportLine);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,107 @@
|
||||
---
|
||||
title: 'Preset Transfer Utility'
|
||||
type: 'feature'
|
||||
created: '2026-05-13'
|
||||
status: 'done'
|
||||
baseline_commit: 'd979c9ee73947175fd246f1048759c74a833133e'
|
||||
context:
|
||||
- '{project-root}/project-context.md'
|
||||
- '{project-root}/AGENTS.md'
|
||||
---
|
||||
|
||||
<frozen-after-approval reason="human-owned intent - do not modify unless human renegotiates">
|
||||
|
||||
## Intent
|
||||
|
||||
**Problem:** HS_DollyCam users need a practical way to move saved camera positions between camera objects, including older versions, without permanently adding memory pressure or dependencies to the main camera scripts.
|
||||
|
||||
**Approach:** Add one isolated drop-in LSL utility script that can be inserted when needed, exports existing `P<idx>` Linkset Data presets as copyable text, imports the same text back from a notecard, and can remove itself afterward.
|
||||
|
||||
## Boundaries & Constraints
|
||||
|
||||
**Always:** Preserve the existing preset payload exactly after the `P<idx>` key; use the current Linkset Data keys `P1`, `P2`, etc.; keep the script independent from controller, menu, playlist, engine, marker, and FOV scripts; restrict commands to the object owner or owner-owned attachments; use owner-visible feedback for all import/export operations.
|
||||
|
||||
**Ask First:** Changing the persisted preset format; adding controller/menu integration; deleting or overwriting unrelated Linkset Data keys; adding a dependency on current-version-only linked-message protocols.
|
||||
|
||||
**Never:** Do not move this into hot-path scripts; do not require edits to old camera versions; do not claim runtime notecard creation is possible in LSL; do not import malformed preset lines silently.
|
||||
|
||||
## I/O & Edge-Case Matrix
|
||||
|
||||
| Scenario | Input / State | Expected Output / Behavior | Error Handling |
|
||||
|----------|--------------|---------------------------|----------------|
|
||||
| Export saved presets | `/88 xfer export 1 20` with `P1` and `P5` populated | Owner receives header, one `P<idx>=<payload>` line per populated preset, and end marker | If range has no presets, report that none were found |
|
||||
| Import presets | `/88 xfer import My Presets` with notecard lines `P1=<payload>` | Script writes each valid line to matching Linkset Data key | Blank, comment, header, and end-marker lines are skipped |
|
||||
| Replace import | `/88 xfer importreplace My Presets` with existing presets | Script clears existing `P1..P999`, then imports valid notecard lines | Missing notecard aborts before clearing |
|
||||
| Malformed import line | Notecard contains `foo` or `P0=<payload>` | Line is rejected and counted as skipped | Owner sees skipped count at completion |
|
||||
| Cleanup | `/88 xfer poof` | Script announces removal and removes its own inventory item | No other inventory or Linkset Data is touched |
|
||||
|
||||
</frozen-after-approval>
|
||||
|
||||
## Code Map
|
||||
|
||||
- `HS_CamController.lsl` -- Existing owner chat and `P<idx>` Linkset Data storage behavior used for compatibility reference.
|
||||
- `project-context.md` -- Project memory and parsing constraints.
|
||||
- `HS_CamPresetTransfer.lsl` -- New isolated drop-in utility script for preset export, import, replace import, status, help, and self-removal.
|
||||
|
||||
## Tasks & Acceptance
|
||||
|
||||
**Execution:**
|
||||
- [x] `HS_CamPresetTransfer.lsl` -- Add standalone transfer script -- Enables import/export without touching core camera scripts or increasing normal runtime memory.
|
||||
- [x] `_bmad-output/implementation-artifacts/spec-preset-transfer-utility.md` -- Track Quick Dev specification and review order -- Keeps the feature reviewable.
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- Given an object with populated `P<idx>` Linkset Data presets, when the owner runs `/88 xfer export [from] [count]`, then the script emits copyable `P<idx>=<payload>` lines that preserve the stored preset payload exactly.
|
||||
- Given an object containing a transfer notecard, when the owner runs `/88 xfer import <notecard>`, then valid `P<idx>=<payload>` lines are written to matching Linkset Data keys without requiring any other HS_DollyCam script.
|
||||
- Given existing presets and a valid notecard, when the owner runs `/88 xfer importreplace <notecard>`, then existing `P1..P999` keys are cleared before import and unrelated Linkset Data keys remain untouched.
|
||||
- Given any command from a non-owner object or avatar, when it is heard on channel 88, then it is ignored.
|
||||
- Given the owner runs `/88 xfer poof`, when the command is accepted, then only the transfer script removes itself.
|
||||
|
||||
## Spec Change Log
|
||||
|
||||
## Design Notes
|
||||
|
||||
The export format intentionally mirrors the Linkset Data key/value pair instead of inventing a richer schema:
|
||||
|
||||
```text
|
||||
HS_DOLLYCAM_PRESETS_V1
|
||||
P1=px|py|pz|fx|fy|fz|rx|ry|rz|rs|fovRad
|
||||
P2=px|py|pz|fx|fy|fz|rx|ry|rz|rs|fovRad
|
||||
HS_DOLLYCAM_PRESETS_END
|
||||
```
|
||||
|
||||
This keeps import compatible with older camera builds because the utility writes the same `P<idx>` keys and unchanged preset value strings those builds already read.
|
||||
|
||||
## Verification
|
||||
|
||||
**Commands:**
|
||||
- `rg -n "llParseString2List" HS_CamPresetTransfer.lsl` -- expected: no output.
|
||||
- Conflict marker scan on `HS_CamPresetTransfer.lsl` and this spec -- expected: no conflict marker output.
|
||||
|
||||
**Manual checks:**
|
||||
- In-world compile in Second Life Viewer or Firestorm; local repository has no official LSL compiler.
|
||||
|
||||
## Suggested Review Order
|
||||
|
||||
**Command Surface**
|
||||
|
||||
- Entry point keeps transfer commands isolated under `/88 xfer`.
|
||||
[`HS_CamPresetTransfer.lsl:240`](../../HS_CamPresetTransfer.lsl#L240)
|
||||
|
||||
- Owner gate matches the existing controller trust model.
|
||||
[`HS_CamPresetTransfer.lsl:304`](../../HS_CamPresetTransfer.lsl#L304)
|
||||
|
||||
**Import And Export**
|
||||
|
||||
- Export emits stable copyable `P<idx>=<payload>` lines.
|
||||
[`HS_CamPresetTransfer.lsl:165`](../../HS_CamPresetTransfer.lsl#L165)
|
||||
|
||||
- Import validates key shape and minimum preset fields before writing.
|
||||
[`HS_CamPresetTransfer.lsl:103`](../../HS_CamPresetTransfer.lsl#L103)
|
||||
|
||||
- Replace mode clears only preset keys before notecard import.
|
||||
[`HS_CamPresetTransfer.lsl:122`](../../HS_CamPresetTransfer.lsl#L122)
|
||||
|
||||
**Lifecycle**
|
||||
|
||||
- Self-removal is explicit and limited to this script.
|
||||
[`HS_CamPresetTransfer.lsl:274`](../../HS_CamPresetTransfer.lsl#L274)
|
||||
Loading…
x
Reference in New Issue
Block a user