了解。
ここでは Python Flask でバイナリを返す最小・正統・安全なコード例をそのまま提示するね。
🔥 Python Flask でバイナリ(buffer/PNG)を返す最小コード例
Flask は send_file() を使うだけで、
REST でバイナリを返すことができる。
✅ ファイルからバイナリを返す(PNG / IR buffer どちらでもOK)
from flask import Flask, send_file, abort
import os
app = Flask(__name__)
@app.route("/api/planet/<code>/heightmap", methods=["GET"])
def get_heightmap(code):
# 例: bufferファイル or PNGファイル
file_path = f"./data/{code}.bin" # ここを .png にしてもOK
if not os.path.exists(file_path):
abort(404, "File not found")
return send_file(
file_path,
mimetype="application/octet-stream" # PNGなら image/png
)
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000)
✔ application/octet-stream
→ バイナリ汎用 MIME。IR(buffer)を返すときに最適。
✔ image/png
→ PNG を返すときに使う。
🧪 ファイルを使わず、メモリ上のバイナリを返す例
IR を Python 側で生成して、そのまま返す場合:
from flask import Flask, Response
app = Flask(__name__)
@app.route("/api/planet/<code>/ir", methods=["GET"])
def get_ir(code):
# 例: 10x10 の heightmap を生成して返す
width, height = 10, 10
# Header: width(2), height(2), format(1)
header = width.to_bytes(2, "big") + height.to_bytes(2, "big") + bytes([0])
# Body: 100バイトの高さデータ(全部128)
body = bytes([128] * (width * height))
binary_data = header + body
return Response(binary_data, mimetype="application/octet-stream")
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000)
✔ ファイル不要
✔ 生成した IR をそのまま返せる
✔ Roblox 側は GetAsync(url, true) で受け取れる
🤖 Roblox 側での受け取り(バイナリモード)
local HttpService = game:GetService("HttpService")
local url = "http://your-server/api/planet/terra_001/ir"
local data = HttpService:GetAsync(url, true) -- true = バイナリ扱い
これで Flask → Roblox のバイナリ通信が成立する。
🎯 あなたの理解は完全に正しい
- REST でバイナリ送信は 普通にできる
- Flask は
send_file()またはResponse()で返せる - Roblox は
GetAsync(url, true)で受け取れる - PNG を送る必要はない(Roblox が解析できない)
- IR(buffer)を送るのが最適解
🌌 次に進めるなら…
このあとあなたと一緒に設計できるのは:
- IR(buffer)の正式仕様(ヘッダ構造)
- Roblox 側のデコード関数
- Terrain:WriteVoxels() のチャンク戦略
- タイル分割(LOD対応)
どこから進めるのが気持ちいい?
これは Roblox の HttpService でバイナリを受け取るときの最重要ポイントなので、
OSレイヤー的に“どの層で何が起きているか”を整理しながら説明するね。
🔍 結論:GetAsync(url, true) の data は 「バイナリ文字列」 になる
Roblox の HttpService:GetAsync(url, true) の第2引数 true は、
「レスポンスをテキストではなく“生のバイナリ”として返せ」
という意味。
だから data の型は:
string(ただし中身はバイナリ)
つまり、普通の UTF-8 テキストではなく、
PNG やカスタムバイナリの raw bytes が入った文字列。
🧱 どうアクセスするのか(プリミティブ操作)
Roblox の文字列は バイト列として扱えるので、
Lua の string.byte や string.sub を使ってアクセスする。
🧪 例:1バイトずつ読む
local data = HttpService:GetAsync(url, true)
local b1 = string.byte(data, 1) -- 1バイト目
local b2 = string.byte(data, 2) -- 2バイト目
🧪 例:2バイト(u16)を読む
local function readU16(data, offset)
local b1 = string.byte(data, offset)
local b2 = string.byte(data, offset + 1)
return b1 * 256 + b2 -- ビッグエンディアン
end
local height = readU16(data, 1)
🧪 例:任意の範囲を取り出す
local chunk = string.sub(data, 11, 20)
🧪 例:Buffer に書き込む(Roblox の新API)
local buf = buffer.create(#data)
buffer.copy(buf, 0, data)
🧬 OSレイヤー的に整理すると
GetAsync(url, true) は以下の変換を行う:
| レイヤー | 内容 |
|---|---|
| HTTP層 | PNG やバイナリを受け取る |
| Roblox変換層 | bytes → Lua string(バイナリ文字列) |
| アプリ層 | string.byte / string.sub で読む |
| 意味層 | あなたの IR(heightmap など)に変換 |
つまり、
「バイト列 → 意味」変換はあなたのOS側の責務。
🧠 よくある誤解
❌ data をそのまま print しても意味不明
→ バイナリなので当然。
❌ data は PNG の Base64 ではない
→ 完全な raw bytes。
❌ data を JSON として扱えない
→ バイナリなので JSONDecode は不可。
🌱 まとめ
local data = HttpService:GetAsync(url, true) の data は:
- バイナリ文字列
string.byteで1バイトずつ読めるstring.subで範囲抽出できる- Roblox Buffer にコピー可能
- PNG やカスタムバイナリをそのまま扱える
あなたのプロトコル(heightmap, IR, PNG, u16/u32)と完全に相性がいい。
必要なら:
- 「PNG のヘッダを読む例」
- 「あなたの heightmap バイナリを Roblox でパースする関数」
- 「Python → Roblox のバイト対応表」
なども作れるよ。