Sicherung

This commit is contained in:
mita 2026-05-20 06:03:40 +02:00
parent d979c9ee73
commit 280c620a15
10 changed files with 4801 additions and 5974 deletions

View File

@ -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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

329
HS_CamPresetTransfer.lsl Normal file
View 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

View File

@ -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)