460d136d21
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).
6.4 KiB
6.4 KiB
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
```