Files
foundryvtt-admin/cobol
octoturge 460d136d21 chore: initial commit of foundryvtt-admin
Spun out of the hbm-books working folder into its own repo. A small
backend+frontend+COBOL admin tool for managing Foundry VTT worlds
(socket inspection, join/shutdown debugging).
2026-08-17 21:46:42 +02:00
..

COBOL Admin Panel Bridge Integration

Welcome to your COBOL learning project! This container is equipped with GnuCOBOL (cobc) and mounts the shared folder at /app/shared.

How the Sync Interface Works

The Node.js proxy server generates /app/shared/ACTORS.DAT containing actor records. Each record is exactly 80 bytes (including a trailing newline \n), with the following fixed-width layout:

Field Size (PIC) Type Offset Description
ID PIC X(a6) Alphanumeric 0 - 15 Foundry Actor Unique ID
NAME PIC X(20) Alphanumeric 16 - 35 Character Name
TYPE PIC X(10) Alphanumeric 36 - 45 "character" or "npc"
HP-CUR PIC 9(3) Numeric 46 - 48 Current HP (zero-padded, e.g. 045)
HP-MAX PIC 9(3) Numeric 49 - 51 Max HP (zero-padded, e.g. 100)
LEVEL PIC 9(2) Numeric 52 - 53 Character Level (zero-padded, e.g. 05)
PADDING PIC X(25) Alphanumeric 54 - 78 Empty padding spaces
NEWLINK PIC X Alphanumeric 79 Carriage return / line feed \n

To Submit Updates

When your COBOL program wants to edit a sheet, it writes the updated 80-byte record into /app/shared/UPDATES.DAT. The Node.js/Bun proxy detects writes to this file, parses the record, pushes the edit to Foundry VTT via WebSocket, and then clears UPDATES.DAT.


COBOL Snippets for your Project

1. Declaring the Files in your Program

       ENVIRONMENT DIVISION.
       INPUT-OUTPUT SECTION.
       FILE-CONTROL.
      *    The source database generated by Node.js
           SELECT ACTORS-FILE ASSIGN TO "/app/shared/ACTORS.DAT"
               ORGANIZATION IS LINE SEQUENTIAL.

      *    The transaction file you write to send updates to Foundry
           SELECT UPDATES-FILE ASSIGN TO "/app/shared/UPDATES.DAT"
               ORGANIZATION IS LINE SEQUENTIAL.
```

### 2. Declaring the Data Record Structure
````cobol
       DATA DIVISION.
       FILE SECTION.
       FD  ACTORS-FILE.
       01  ACTOR-RECORD.
           05  ACTOR-ID        PIC X(16).
           05  ACTOR-NAME      PIC X(20).
           05  ACTOR-TYPE     PIC X(10).
           05  ACTOR-HP-CUR    PIC 9(3).
           05  ACTOR-HP-MAX    PIC 9(3).
           05  ACTOR-LEVEL     PIC 9(2).
           05  FILLER          PIC X(25). *> Filler for padding
```

### 3. Reading the File Loop
````cobol
       WORKING-STORAGE SECTION.
       01  WS-EOF-FLAG         PIC X VALUE 'N'.
           88  EOF-REACHED           VALUE 'Y'.

       PROCEDURE DIVISION.
       READ-DATABASE.
           OPEN INPUT ACTORS-FILE.
           PERFORM UNTIL EOF-REACHED
               READ ACTORS-FILE
                   AT END
                       SET EOF-REACHED TO TRUE
                   NOT AT END
                       DISPLAY "Found: " ACTOR-NAME " (HP: " ACTOR-HP-CUR ")"
               END-READ
           END-PERFORM.
           CLOSE ACTORS-FILE.
```

### 4. Writing an Update (Transaction)
````cobol
       PROCEDURE DIVISION.
       SEND-UPDATE.
           OPEN OUTPUT UPDATES-FILE.
      *    Fill the record fields (ensure numeric values are zero-padded)
           MOVE "abc123xyz789"  TO ACTOR-ID.
           MOVE "Gimli"         TO ACTOR-NAME.
           MOVE "character"     TO ACTOR-TYPE.
           MOVE 45             TO ACTOR-HP-CUR.
           MOVE 100            TO ACTOR-HP-MAX.
           MOVE 5              TO ACTOR-LEVEL.
           
           WRITE ACTOR-RECORD.
           CLOSE UPDATES-FILE.
```

### 5. Alternative: Calling curl Directly from COBOL (REST API Access)
If your want to bypass files and hit the Node.js REST API directly from your COBOL program, GnuCOBOL provides the SYSTEM command:
```cobol
       WORKING-STORAGE SECTION.
       01  API-COMMAND         PIC X(200).
       
       PROCEDURE DIVISION.
       TRIGGER-API.
      *    Using curl inside the Docker network to fetch the JSON list
       STRING "curl -s http://backend:3000/api/actors > /app/shared/actors.json"
           DELIMITED BY SIZE INTO API-COMMAND.
               
           CALL "SYSTEM" USING API-COMMAND.
```

---

## Compiling & Running Inside Docker
Attach to the running COBOL container shell:
```bash
docker compose exec -it cobol sh
```

Create your file hbm-admin.cob, then compile it:
```bash
cobc -x -o hbm-admin hbm-admin.cob
```
Run the compiled executable:
```bash
./hbm-admin
```

---

### 6. Native C Bindings (Interfacing with libcurl)
Since GnuCOBOL translates COBOL code to C, you can interface with C libraries directly using the `CALL` statement. This allows you to perform real HTTP requests natively without spawning an external shell process.

1. **Write a C Helper (`http_client.c`)**:
```c
#include <stdio.h>
#include <curl/curl.h>

int send_actor_update(const char* url, const char* json_data) {
    CURL *curl;
    CURLcode res;
    int success = 1;

    curl = curl_easy_init();
    if(curl) {
        struct curl_slist *headers = NULL;
        headers = curl_slist_append(headers, "Content-Type: application/json");

        curl_easy_setopt(curl, CURLOPT_URL, url);
        curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
        curl_easy_setopt(curl, CURLOPT_POSTFIELDS, json_data);

        res = curl_easy_perform(curl);
        if(res != CURLE_OK) {
            fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
            success = 0;
        }

        curl_slist_free_all(headers);
        curl_easy_cleanup(curl);
    }
    return success;
}
```

2. **Call it from COBOL (`hbm-admin.cob`)**:
```cobol
       WORKING-STORAGE SECTION.
       01  URL-STRING          PIC X(100) VALUE "http://backend:3000/api/actors/update".
       01  JSON-PAYLOAD        PIC X(500).
       01  RESULT-CODE         PIC S9(9) BINARY.

       PROCEDURE DIVISION.
           MOVE "{\"id\":\"abc\",\"updates\":{\"system\":{\"attributes\":{\"hp\":{\"value\":45}}}}}" 
               TO JSON-PAYLOAD.
               
           CALL "send_actor_update" USING BY REFERENCE URL-STRING
                                          BY REFERENCE JSON-PAYLOAD
                                    RETURNING RESULT-CODE.
                                    
           IF RESULT-CODE = 1
               DISPLAY "Update successful!"
           ELSE
               DISPLAY "Update failed."
           END-IF.
```

3. **Compile and link together**:
```bash
gcc -c http_client.c -o http_client.o
cobc -x -o hbm-admin hbm-admin.cob http_client.o -lcurl
```