Sicherung
This commit is contained in:
parent
d979c9ee73
commit
280c620a15
@ -20,8 +20,8 @@ focus_threshold=0.02
|
|||||||
# TourEngine internal update throttle.
|
# TourEngine internal update throttle.
|
||||||
# 0.033 ~= 30 Hz camera updates while still computing the path at move_step.
|
# 0.033 ~= 30 Hz camera updates while still computing the path at move_step.
|
||||||
tour_cam_min_interval=0.033
|
tour_cam_min_interval=0.033
|
||||||
tour_pos_epsilon=0.005
|
tour_pos_epsilon=0.002
|
||||||
tour_focus_epsilon=0.005
|
tour_focus_epsilon=0.002
|
||||||
|
|
||||||
# Follow smoothing aid
|
# Follow smoothing aid
|
||||||
follow_predict=0.10
|
follow_predict=0.10
|
||||||
|
|||||||
1570
HS_CamEngineCore.lsl
1570
HS_CamEngineCore.lsl
File diff suppressed because it is too large
Load Diff
1785
HS_CamEngineTour.lsl
1785
HS_CamEngineTour.lsl
File diff suppressed because it is too large
Load Diff
1028
HS_CamFov.lsl
1028
HS_CamFov.lsl
File diff suppressed because it is too large
Load Diff
1005
HS_CamMarkers.lsl
1005
HS_CamMarkers.lsl
File diff suppressed because it is too large
Load Diff
1688
HS_CamMenu.lsl
1688
HS_CamMenu.lsl
File diff suppressed because it is too large
Load Diff
1766
HS_CamPlaylist.lsl
1766
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