new service files - updates to ll-stack-architecture.md

This commit is contained in:
2026-08-25 09:48:34 +02:00
parent 14f888843f
commit 6c8c89ffce
18 changed files with 644 additions and 34 deletions

View File

@@ -0,0 +1,32 @@
[Unit]
Description=llama.cpp server - alpha (meta/combat/action_eval on Ada #1)
After=network-online.target
[Service]
Type=simple
User=nyx-organs
Group=nimmerverse-agents
Environment="CUDA_VISIBLE_DEVICES=0"
Environment="PATH=/usr/local/cuda/bin:/data/venvs/llama/bin:/usr/local/bin:/usr/bin:/usr/sbin:/bin"
ExecStart=/data/llama.cpp/build/bin/llama-server \
--model /data/models/prod/alpha/Gemma4-31B-QAT-Uncensored-HauhauCS-Balanced-Q4_K_M.gguf \
--model-draft /data/models/prod/alpha/mtp-gemma-4-31B-it.gguf \
--spec-type draft-mtp \
-fa on \
--alias alpha \
--host 0.0.0.0 \
--port 31001 \
--n-gpu-layers 40 \
--ctx-size 65536 \
--cache-type-k q4_0 \
--cache-type-v q4_0 \
--batch-size 2048 \
--ubatch-size 512 \
--threads 8 \
--cont-batching \
--reasoning off
RestartSec=5
WorkingDirectory=/data
[Install]
WantedBy=multi-user.target

View File

@@ -0,0 +1,32 @@
[Unit]
Description=llama.cpp server - beta (meta/combat/action_eval on Ada #1)
After=network-online.target
[Service]
Type=simple
User=nyx-organs
Group=nimmerverse-agents
Environment="CUDA_VISIBLE_DEVICES=1"
Environment="PATH=/usr/local/cuda/bin:/data/venvs/llama/bin:/usr/local/bin:/usr/bin:/usr/sbin:/bin"
ExecStart=/data/llama.cpp/build/bin/llama-server \
--model /data/models/prod/beta/Gemma4-31B-QAT-Uncensored-HauhauCS-Balanced-Q4_K_M.gguf \
--model-draft /data/models/prod/beta/mtp-gemma-4-31B-it.gguf \
--spec-type draft-mtp \
-fa on \
--alias beta \
--host 0.0.0.0 \
--port 31002 \
--n-gpu-layers 40 \
--ctx-size 65536 \
--cache-type-k q4_0 \
--cache-type-v q4_0 \
--batch-size 2048 \
--ubatch-size 512 \
--threads 8 \
--cont-batching \
--reasoning off
RestartSec=5
WorkingDirectory=/data
[Install]
WantedBy=multi-user.target

View File

@@ -0,0 +1,31 @@
[Unit]
Description=llama.cpp server - delta
After=network-online.target
[Service]
Type=simple
User=nyx-organs
Group=nimmerverse-agents
Environment="PATH=/usr/local/cuda/bin:/data/venvs/llama/bin:/usr/local/bin:/usr/bin:/usr/sbin:/bin"
ExecStart=/usr/local/bin/llama-server \
--model /data/models/prod/delta/Gemma4-31B-QAT-Uncensored-HauhauCS-Balanced-Q4_K_M.gguf \
--model-draft /data/models/prod/delta/mtp-gemma-4-31B-it.gguf \
--spec-type draft-mtp \
-fa on \
--alias delta \
--host 0.0.0.0 \
--port 31001 \
--n-gpu-layers 60 \
--cache-type-k q4_0 \
--cache-type-v q4_0 \
--ctx-size 262144 \
--batch-size 2048 \
--ubatch-size 512 \
--threads 8 \
--cont-batching \
--reasoning off
RestartSec=5
WorkingDirectory=/data
[Install]
WantedBy=multi-user.target

View File

@@ -0,0 +1,31 @@
[Unit]
Description=llama.cpp server - epsilon
After=network-online.target
[Service]
Type=simple
User=nyx-organs
Group=nimmerverse-agents
Environment="PATH=/usr/local/cuda/bin:/data/venvs/llama/bin:/usr/local/bin:/usr/bin:/usr/sbin:/bin"
ExecStart=/usr/local/bin/llama-server \
--model /data/models/prod/epsilon/Gemma4-31B-QAT-Uncensored-HauhauCS-Balanced-Q4_K_M.gguf \
--model-draft /data/models/prod/epsilon/mtp-gemma-4-31B-it.gguf \
--spec-type draft-mtp \
-fa on \
--alias epsilon \
--host 0.0.0.0 \
--port 31002 \
--n-gpu-layers 60 \
--cache-type-k q4_0 \
--cache-type-v q4_0 \
--ctx-size 262144 \
--batch-size 2048 \
--ubatch-size 512 \
--threads 8 \
--cont-batching \
--reasoning off
RestartSec=5
WorkingDirectory=/data
[Install]
WantedBy=multi-user.target

View File

@@ -0,0 +1,31 @@
[Unit]
Description=llama.cpp server - gamma (memory/profile/vision/translator on RTX 3090)
After=network-online.target
[Service]
Type=simple
User=nyx-organs
Group=nimmerverse-agents
Environment="PATH=/usr/local/cuda/bin:/data/venvs/llama/bin:/usr/local/bin:/usr/bin:/usr/sbin:/bin"
ExecStart=/data/llama.cpp/build/bin/llama-server \
--model /data/models/prod/gamma/Gemma4-31B-QAT-Uncensored-HauhauCS-Balanced-Q4_K_M.gguf \
--model-draft /data/models/prod/gamma/mtp-gemma-4-31B-it.gguf \
--spec-type draft-mtp \
-fa on \
--alias gamma \
--host 0.0.0.0 \
--port 31003 \
--n-gpu-layers 40 \
--ctx-size 262144 \
--cache-type-k q4_0 \
--cache-type-v q4_0 \
--batch-size 2048 \
--ubatch-size 512 \
--threads 8 \
--cont-batching \
--reasoning off
RestartSec=5
WorkingDirectory=/data
[Install]
WantedBy=multi-user.target

View File

@@ -0,0 +1,23 @@
[Unit]
Description=llama.cpp server - zeta
After=network-online.target
[Service]
Type=simple
User=nyx-organs
Group=nimmerverse-agents
Environment="PATH=/usr/local/cuda/bin:/data/venvs/llama/bin:/usr/local/bin:/usr/bin:/usr/sbin:/bin"
ExecStart=llama-server \
--model /data/models/prod/zeta/Huihui-Qwen3-VL-8B-Instruct-abliterated.Q8_0.gguf \
--alias zeta \
--mmproj /data/models/prod/zeta/zeta-mmproj-Q8_0.gguf \
--host 0.0.0.0 \
--port 31003 \
--n-gpu-layers 9999 \
--ctx-size 64000 \
--chat-template-kwargs '{"enable_thinking": false}'
RestartSec=5
WorkingDirectory=/data
[Install]
WantedBy=multi-user.target

View File

@@ -5,44 +5,42 @@ Chat endpoint: `/v1/chat/completions` with `{"chat_template_kwargs": {"enable_th
## Instances ## Instances
| Instance | Node | GPU | Port | Model | Quant | VRAM | Vision | | Instance | Node | GPU | Port | Model | Quant | Vision |
|----------|------|-----|------|-------|-------|------|--------| |----------|------|-----|------|-------|-------|------|
| **nyx** | theia | Blackwell 6000 | 31000 | self-trained ~41B | BF16 | ~84GB | ✅ | | **alpha** | dioscuri | Ada #1 | 31001 | Gemma4-31B-QAT-Uncensored-HauhauCS-Balanced-Q4_K_M.gguf-MTP | Q4_K_M MTP
| **alpha1** | dioscuri | Ada #1 | 31001 | Qwen3.6-27B-Fable-711 | IQ4_XS | 16.6GB | ❌ | | **beta** | dioscuri | Ada #2 | 31002 | Gemma4-31B-QAT-Uncensored-HauhauCS-Balanced-Q4_K_M.gguf-MTP | Q4_K_M MTP
| **alpha2** | dioscuri | Ada #2 | 31002 | Qwen3.6-27B-Fable-711 | IQ4_XS | 16.6GB | ❌ | | **gamma** | comfy-dev | RTX 3090 | 31003 | Gemma4-31B-QAT-Uncensored-HauhauCS-Balanced-Q4_K_M.gguf-MTP | Q4_K_M MTP
| **gamma** | comfy-dev | RTX 3090 | 31003 | Qwen3.6-27B-Fable-711-MTP | Q4_K_S MTP | 19.9GB | ✅ | | **delta** | theia | Blackwell 6000 | 31001 | Gemma4-31B-QAT-Uncensored-HauhauCS-Balanced-Q4_K_M.gguf-MTP | Q4_K_M MTP
| **epsilon** | theia | Blackwell 6000 | 31002 | Gemma4-31B-QAT-Uncensored-HauhauCS-Balanced-Q4_K_M.gguf-MTP | Q4_K_M MTP
| **zeta** | theia | Blackwell 6000 | 31003 | Huihui-Qwen3-VL-8B-Instruct-abliterated.Q8_0.gguf | Q8_0 | mmproj-Q8_0.gguf
## Role Mapping ## Service Files under:
/nimmerverse/nimmersky/guides-stack/
- **nyx (31000):** default dialogue, diary, agent_helper llama-alpha.service
- **alpha1 (31001):** meta, combat, action_evaluation llama-beta.service
- **alpha2 (31002):** gamemaster, overflow llama-delta.service
- **gamma (31003):** memory, character_profile, vision, universal_translator llama-epsilon.service
llama-gamma.service
llama-zeta.service
##
## Theia specific
## Endpoints for SkyrimNet sudo nvidia-cuda-mps-control -d
```yaml This command starts the CUDA Multi-Process Service (MPS) control daemon in the background.
default: 10.0.40.21:31000/v1
diary: 10.0.40.21:31000/v1
agent_helper: 10.0.40.21:31000/v1
meta: 10.0.40.22:31001/v1
combat: 10.0.40.22:31001/v1
action_evaluation: 10.0.40.22:31001/v1
gamemaster: 10.0.40.22:31002/v1
memory: 10.0.30.124:31003/v1
character_profile: 10.0.30.124:31003/v1
vision: 10.0.30.124:31003/v1
universal_translator: 10.0.30.124:31003/v1
```
## Notes The control program used to manage the MPS server system.-d (Daemon mode): This flag launches the MPS control daemon in the background.What it does: Normally, if multiple Linux processes try to use the same GPU simultaneously, the GPU time-slices between them (hardware context switching), which adds overhead. MPS allows multiple different processes to share a single GPU context. This lets them execute kernels simultaneously on the same GPU, filling up underutilized GPU hardware and improving overall throughput.
##
- Throughput: gamma ~20 tok/s, alpha1 ~10 tok/s, alpha2 ~14 tok/s sudo nvidia-smi -i 0 -c EXCLUSIVE_PROCESS
- alpha1/alpha2 identical models (SHA256 confirmed)
- gamma has MTP quant; alpha1/alpha2 have non-MTP
- All llama.cpp services run as nyx-organs:nimmerverse-agents
- OStimNet: Fable-Fusion needs explicit x-rated vocabulary in system prompts
## Service Files This command changes the Compute Mode of a specific GPU so that only one process can attach to it at a time.
`~/tmp/skyrimnet-analysis/services/` — llama-gamma.service, llama-alpha1.service, llama-alpha2.service nvidia-smi: The NVIDIA System Management Interface utility.
-i 0 (Index 0): Targets the specific GPU with an ID of 0.
-c EXCLUSIVE_PROCESS (Compute Mode): Sets the compute mode to "Exclusive Process"
.What it does: It restricts the GPU so that only one single CUDA context (process) can access it at any given time. If a second process tries to use GPU 0 while the first is running, it will immediately throw an out-of-memory or initialization error.
##
💡 How They Work TogetherAt first glance, these two commands seem to contradict each other: one allows sharing (MPS), and the other forbids sharing (EXCLUSIVE_PROCESS).However, they are designed to be used together. When you set a GPU to EXCLUSIVE_PROCESS, the MPS server counts as that single allowed process.EXCLUSIVE_PROCESS blocks all regular user applications from touching GPU 0 directly.MPS daemon steps in as the sole owner of GPU 0.Your applications then talk to the MPS daemon, which safely multiplexes their workloads onto the GPU together.This specific combination is the recommended way to deploy MPS because it prevents rogue, non-MPS processes from accidentally hijacking the GPU and ruining performance.

View File

@@ -0,0 +1,270 @@
unit ExportScripts;
//version 1.0
// Global variables
var ArmoList : TStringList;
var ArmaList : TStringList;
var ArmoListSlot : TStringList;
var ArmaListSlot : TStringList;
var RetrieveAddRaces : Boolean;
// Called when the script starts
function Initialize : integer;
var
csvHeaderArmo: string;
csvHeaderArma: string;
begin
csvHeaderArma := 'Plugin;RecordType;FormID;EditorID;Slot;Race;MaleModel;FemaleModel;MaleModel1stPerson;FemaleModel1stPerson;AdditionalRaces';
csvHeaderArmo := 'Plugin;RecordType;FormID;EditorID;Slot;Race;Name;MaleModel;FemaleModel;Type;Enchantment;ArmorRating;Weight;Value;Keywords;ArmorAddon';
ArmoList := TStringList.Create;
ArmoList.Add(csvHeaderArmo);
ArmaList := TStringList.Create;
ArmaList.Add(csvHeaderArma);
ArmoListSlot := TStringList.Create;
ArmoListSlot.Add(csvHeaderArmo);
ArmaListSlot := TStringList.Create;
ArmaListSlot.Add(csvHeaderArma);
//don't retrieve 'Additional Races', takes too long
RetrieveAddRaces := False
end;
// Called for each selected record in the TES5Edit tree
function Process(e: IInterface): integer;
var
i: integer;
slot: cardinal;
espName: string;
recordType: string;
itemName: string;
maleModel: string;
femaleModel: string;
armorType: string;
nArmorType: integer;
typesList: array[0..2] of string;
enchantment: string;
rating: float;
weight: float;
value: integer;
maleModel1st: string;
femaleModel1st: string;
keywords: string;
armature: string;
elKwda: IInterface;
elArma: IInterface;
elRaces: IInterface;
slotList: TStringList;
formId: string;
primaryRace: string;
additionalRaces: string;
begin
if ((Signature(e) <> 'ARMO') and (Signature(e) <> 'ARMA')) then Exit;
typesList[0] := 'Light';
typesList[1] := 'Heavy';
typesList[2] := 'None';
slotList := TStringList.Create;
itemName := '';
armorType := '';
maleModel := '';
femaleModel := '';
enchantment := '';
maleModel1st := '';
femaleModel1st := '';
keywords := '';
rating := 0.0;
weight := 0.0;
value := 0.0;
formId := IntToHex(GetLoadOrderFormID(e), 8);
slotList.Delimiter := ',';
primaryRace := '';
espName := '[' + IntToStr(GetLoadOrder(GetFile(e))) + '] ' + GetFileName(e);
if ElementExists(e,'FULL') then itemName := GetElementEditValues(e, 'FULL');
if ElementExists(e,'EITM') then enchantment := GetElementEditValues(e, 'EITM');
if ElementExists(e,'DNAM') then rating := GetElementNativeValues(e, 'DNAM');
if ElementExists(e,'DATA\Value') then value := GetElementNativeValues(e, 'DATA\Value');
if ElementExists(e,'DATA\Weight') then weight := GetElementNativeValues(e, 'DATA\Weight');
if ElementExists(e,'RNAM') then primaryRace := GetElementEditValues(e, 'RNAM');
if ElementExists(e,'BODT\First Person Flags') then
begin
slot := GetElementNativeValues(e, 'BODT\First Person Flags');
if ElementExists(e,'BODT\Armor Type') then
begin
nArmorType := GetElementNativeValues(e, 'BODT\Armor Type');
armorType := typesList[nArmorType];
end
end
else
begin
if ElementExists(e,'BOD2\First Person Flags') then
begin
slot := GetElementNativeValues(e, 'BOD2\First Person Flags');
if ElementExists(e,'BOD2\Armor Type') then
begin
nArmorType := GetElementNativeValues(e, 'BOD2\Armor Type');
armorType := typesList[nArmorType];
end
end
else
begin
AddMessage('********* element doesnt have slot definition *********');
Exit;
end
end;
if Signature(e) = 'ARMA' then
begin
if ElementExists(e,'Male world model\MOD2') then maleModel := GetElementNativeValues(e, 'Male world model\MOD2');
if ElementExists(e,'Female world model\MOD3') then femaleModel := GetElementNativeValues(e, 'Female world model\MOD3');
if ElementExists(e,'Male 1st Person\MOD4') then maleModel1st := GetElementNativeValues(e, 'Male 1st Person\MOD4');
if ElementExists(e,'Female 1st Person\MOD5') then femaleModel1st := GetElementNativeValues(e, 'Female 1st Person\MOD5');
if RetrieveAddRaces and ElementExists(e,'Additional Races') then
begin
elRaces := ElementByName(e, 'Additional Races');
for i := 0 to ElementCount(elRaces) - 1 do begin
additionalRaces := additionalRaces + GetElementEditValues(LinksTo(ElementByIndex(elRaces,i)), 'EDID');
if(i < (ElementCount(elRaces) - 1)) then additionalRaces := additionalRaces + ',';
end;
end;
end
else
begin
if Signature(e) = 'ARMO' then
begin
if ElementExists(e,'Male world model\MOD2') then maleModel := GetElementNativeValues(e, 'Male world model\MOD2');
if ElementExists(e,'Female world model\MOD4') then femaleModel := GetElementNativeValues(e, 'Female world model\MOD4');
if ElementExists(e,'KWDA') then
begin
elKwda := ElementBySignature(e, 'KWDA');
for i := 0 to ElementCount(elKwda) - 1 do begin
keywords := keywords + GetElementEditValues(LinksTo(ElementByIndex(elKwda,i)), 'EDID');
if(i < (ElementCount(elKwda) - 1)) then keywords := keywords + ',';
end;
end;
if ElementExists(e,'Armature') then
begin
elArma := ElementByName(e, 'Armature');
for i := 0 to ElementCount(elArma) - 1 do begin
armature := armature + GetElementEditValues(LinksTo(ElementByIndex(elArma,i)), 'EDID');
if(i < (ElementCount(elArma) - 1)) then armature := armature + ',';
end;
end;
end
end;
//loop through the 32 slots
i := 1;
while(i <= 32) do
begin
i := i + 1;
//bitwise and
if ((slot and 1) = 1) then
begin
slotList.Add(IntToStr(i+28));
AddMessage(espName + ';' + Signature(e) + ';' + formId +';'+ GetElementEditValues(e, 'EDID') + ';' + itemName + ';' + maleModel + ';' + femaleModel +';' + armorType +';' + enchantment + ';' + FloatToStr(rating/100) + ';' + FloatToStr(weight) + ';' + IntToStr(value) + ';' + IntToStr(i+28) + ';' + keywords);
if Signature(e) = 'ARMO' then
ArmoListSlot.Add(espName + ';'
+ Signature(e) + ';'
+ formId + ';'
+ GetElementEditValues(e, 'EDID') + ';'
+ IntToStr(i+28) + ';'
+ primaryRace + ';'
+ itemName + ';'
+ maleModel + ';'
+ femaleModel +';'
+ armorType +';'
+ enchantment + ';'
+ FloatToStr(rating/100) + ';'
+ FloatToStr(weight) + ';'
+ IntToStr(value) + ';'
+ keywords + ';'
+ armature
);
if Signature(e) = 'ARMA' then
ArmaListSlot.Add(espName + ';'
+ Signature(e) + ';'
+ formId + ';'
+ GetElementEditValues(e, 'EDID') + ';'
+ IntToStr(i+28) + ';'
+ primaryRace + ';'
+ maleModel + ';'
+ femaleModel +';'
+ maleModel1st + ';'
+ femaleModel1st + ';'
+ additionalRaces
);
end
slot := slot shr 1;
end;
if Signature(e) = 'ARMO' then
ArmoList.Add(espName + ';'
+ Signature(e) + ';'
+ formId + ';'
+ GetElementEditValues(e, 'EDID') + ';'
+ slotList.DelimitedText + ';'
+ primaryRace + ';'
+ itemName + ';'
+ maleModel + ';'
+ femaleModel +';'
+ armorType +';'
+ enchantment + ';'
+ FloatToStr(rating/100) + ';'
+ FloatToStr(weight) + ';'
+ IntToStr(value) + ';'
+ keywords + ';'
+ armature
);
if Signature(e) = 'ARMA' then
ArmaList.Add(espName + ';'
+ Signature(e) + ';'
+ formId + ';'
+ GetElementEditValues(e, 'EDID') + ';'
+ slotList.DelimitedText + ';'
+ primaryRace + ';'
+ maleModel + ';'
+ femaleModel +';'
+ maleModel1st + ';'
+ femaleModel1st + ';'
+ additionalRaces
);
slotList.Free;
end;
// Called after the script has finished processing every record
function Finalize : integer;
var filenameArmo : string;
var filenameArma : string;
var filenameArmoSlot : string;
var filenameArmaSlot : string;
begin
filenameArmo := ProgramPath + 'ArmorExport-Armor.csv';
filenameArma := ProgramPath + 'ArmorExport-ArmorAddon.csv';
filenameArmoSlot := ProgramPath + 'ArmorExport-Armor-Slot.csv';
filenameArmaSlot := ProgramPath + 'ArmorExport-ArmorAddon-Slot.csv';
AddMessage('Saving Armor list to ' + filenameArmo);
AddMessage('Saving ArmorAddon list to ' + filenameArma);
AddMessage('Saving Armor-Slot list to ' + filenameArmoSlot);
AddMessage('Saving ArmorAddon-Slot list to ' + filenameArmaSlot);
ArmoList.SaveToFile(filenameArmo);
ArmoList.Free;
ArmaList.SaveToFile(filenameArma);
ArmaList.Free;
ArmoListSlot.SaveToFile(filenameArmoSlot);
ArmoListSlot.Free;
ArmaListSlot.SaveToFile(filenameArmaSlot);
ArmaListSlot.Free;
end;
end.

View File

@@ -0,0 +1,6 @@
param([Parameter(Mandatory=$true)][string[]]$keyword)
### keyword to filter
$csvDir=(Join-Path $PSScriptRoot ..)
Get-Content $csvDir\ArmorExport-Armor.csv|convertFrom-Csv -Delimiter ';' `
|ForEach-Object { $_.Keywords = ($_.Keywords -split ','); $_ } `
|where-object { $keyword -in $_.Keywords }

View File

@@ -0,0 +1,4 @@
param([Parameter(Mandatory=$true)][int]$slot)
#slot to filter
$csvDir=(Join-Path $PSScriptRoot ..)
Get-Content $csvDir\ArmorExport-Armor-Slot.csv|ConvertFrom-Csv -Delimiter ';'|Sort-Object -Property FormID,Slot -Unique|Where-Object { $_.Slot -eq $slot }

View File

@@ -0,0 +1,2 @@
$csvDir=(Join-Path $PSScriptRoot ..)
Get-Content $csvDir\ArmorExport-Armor.csv|convertFrom-Csv -Delimiter ';'|ForEach-Object { $_.Keywords = ($_.Keywords -split ','); $_ }|where-object { $_.Keywords -contains 'ArmorHeavy' }

View File

@@ -0,0 +1,2 @@
$csvDir=(Join-Path $PSScriptRoot ..)
Get-Content $csvDir\ArmorExport-Armor.csv|convertFrom-Csv -Delimiter ';'|ForEach-Object { $_.Keywords = ($_.Keywords -split ','); $_ }|where-object { $_.Keywords -contains 'ArmorLight' }

View File

@@ -0,0 +1,3 @@
#show count of records by slot
$csvDir=(Join-Path $PSScriptRoot ..)
Get-Content $csvDir\ArmorExport-Armor-Slot.csv|ConvertFrom-Csv -Delimiter ';'|Sort-Object -Property FormID,Slot -Unique|Sort-Object Slot|Group-Object -NoElement -Property slot

View File

@@ -0,0 +1,3 @@
param([Parameter(Mandatory=$true)][int]$slot)
$csvDir=(Join-Path $PSScriptRoot ..)
Get-Content $csvDir\ArmorExport-ArmorAddon-Slot.csv|ConvertFrom-Csv -Delimiter ';'|Sort-Object -Property FormID,Slot -Unique|Where-Object { $_.Slot -eq $slot }

View File

@@ -0,0 +1,3 @@
#show count of records by slot
$csvDir=(Join-Path $PSScriptRoot ..)
Get-Content $csvDir\ArmorExport-ArmorAddon-Slot.csv|ConvertFrom-Csv -Delimiter ';'|Sort-Object -Property FormID,Slot -Unique|Sort-Object Slot|Group-Object -NoElement -Property slot

View File

@@ -0,0 +1,51 @@
<#
.PARAMETER editorId
to filter by editorId
.PARAMETER formId
to filter by formId
.PARAMETER all
to list all records
.PARAMETER slotAsArray
convert slot field to array
.PARAMETER splitBySlot
split records by slot
.EXAMPLE
PS> .\get-armor.ps1 -editorId EnchArmorImperialLightGauntletsMarksman03
.EXAMPLE
PS> .\get-armor.ps1 -formId 000B9745
#>
param([string]$editorId, [string]$formId, [switch]$slotAsArray, [switch]$splitBySlot)
#editorId or formId to filter
#usage:
## .\get-armor.ps1 -editorId EnchArmorImperialLightGauntletsMarksman03
## .\get-armor.ps1 -formId 000B9745
$csvDir=(Join-Path $PSScriptRoot ..)
$csvFile="ArmorExport-Armor.csv"
if($splitBySlot) {
$csvFile="ArmorExport-Armor-Slot.csv"
}
if($editorId -or $formId) {
Get-Content (Join-Path $csvDir $csvFile)|ConvertFrom-Csv -Delimiter ';' `
|where-object { ($editorId -and $_.EditorID -eq $editorId) -or ($formId -and $_.FormID -eq $formId)} `
|foreach-object {
$_.Keywords = $_.Keywords -Split ',';
$_.ArmorAddon = $_.ArmorAddon -Split ',';
if($slotAsArray -and !$splitBySlot) { $_.Slot = ($_.Slot -Split ','); }
$_
}
} else {
Get-Content (Join-Path $csvDir $csvFile)|ConvertFrom-Csv -Delimiter ';' `
|foreach-object {
$_.Keywords = $_.Keywords -Split ',';
$_.ArmorAddon = $_.ArmorAddon -Split ',';
if($slotAsArray -and !$splitBySlot) { $_.Slot = ($_.Slot -Split ','); }
$_
}
}

View File

@@ -0,0 +1,88 @@
<#
.PARAMETER editorId
to filter by editorId
.PARAMETER formId
to filter by formId
.PARAMETER all
to list all records
.PARAMETER slotAsArray
convert slot field to array
.PARAMETER splitBySlot
split records by slot
.EXAMPLE
PS> .\get-armor.ps1 | ?{ $_.FormID -in '00013939' } | .\get-armoraddon.ps1
.EXAMPLE
PS> .\get-armoraddon.ps1 -formId 0005DB82
.EXAMPLE
PS> .\get-armor.ps1 -editorId EnchArmorImperialLightGauntletsMarksman03|.\get-armoraddon.ps1
.EXAMPLE
PS> .\armor-by-keyword.ps1 ArmorHeavy|?{$_.Keywords -contains 'ArmorHelmet'}|.\get-armoraddon.ps1
#>
param([string]$editorId, [string]$formId, [switch]$all, [switch]$slotAsArray, [switch]$splitBySlot)
# editorId or formId to filter
# -all to list all records
#usage:
## .\get-armor.ps1 | ?{ $_.FormID -in '00013939' } | .\get-armoraddon.ps1
## .\get-armoraddon.ps1 -formId 0005DB82
## .\get-armor.ps1 -editorId EnchArmorImperialLightGauntletsMarksman03|.\get-armoraddon.ps1
## .\armor-by-keyword.ps1 ArmorHeavy|?{$_.Keywords -contains 'ArmorHelmet'}|.\get-armoraddon.ps1
Set-StrictMode -Version 3.0
$csvDir=(Join-Path $PSScriptRoot ..)
$myInput = $input
$ids=@()
$inObjs=@{}
foreach($i in $myInput) {
foreach($a in $i.ArmorAddon) {
$ids+=$a;
if($inObjs.ContainsKey($a)) {
$inObjs[$a]=$i
} else {
$inObjs.Add($a, $i)
}
}
}
$csvDir=(Join-Path $PSScriptRoot ..)
$csvFile="ArmorExport-ArmorAddon.csv"
if($splitBySlot) {
$csvFile="ArmorExport-ArmorAddon-Slot.csv"
}
if($editorId -or $formId) {
Get-Content (Join-Path $csvDir $csvFile)|ConvertFrom-Csv -Delimiter ';' `
|where-object { ($editorId -and $_.EditorID -eq $editorId) -or ($formId -and $_.FormID -eq $formId)} `
|foreach-object { `
if($slotAsArray -and !$splitBySlot) { $_.Slot = ($_.Slot -Split ','); } `
$_.AdditionalRaces = ($_.AdditionalRaces -Split ','); `
$_ `
}
} elseif($ids.Length -gt 0) {
Get-Content (Join-Path $csvDir $csvFile)|ConvertFrom-Csv -Delimiter ';' `
|where-object { $ids -contains $_.EditorID } `
|foreach-object { `
if($slotAsArray -and !$splitBySlot) { $_.Slot = ($_.Slot -Split ','); } `
$_.AdditionalRaces = ($_.AdditionalRaces -Split ','); `
$_ } `
|foreach-object { Add-Member -InputObject $_ -MemberType NoteProperty -Name _ArmoRef `
-Value "$($inObjs[$_.EditorID].EditorID) `"$($inObjs[$_.EditorID].Name)`" [$($inObjs[$_.EditorID].RecordType):$($inObjs[$_.EditorID].FormID)]"; `
$_ }
} elseif($all) {
Get-Content (Join-Path $csvDir $csvFile)|ConvertFrom-Csv -Delimiter ';' `
|foreach-object { `
if($slotAsArray -and !$splitBySlot) { $_.Slot = ($_.Slot -Split ','); } `
$_.AdditionalRaces = ($_.AdditionalRaces -Split ','); `
$_ }
}

View File

@@ -1 +1 @@
traits: direct - boisterous - patriarchal - revanchist - warm to kin, wary of outsiders traits: direct - boisterous - patriarchal - revanchist - warm to kin, openly racist to outsiders