KO UO Topic http - LIHACHTETAN/ClassicUO-Bad-Newbie-CSharp-Scripts-IDE GitHub Wiki

ะฃะบั€ะฐั—ะฝััŒะบะฐ ยท ะ ัƒััะบะธะน ยท English ยท Deutsch ยท Franรงais ยท Italiano ยท Espaรฑol ยท ็น้ซ”ไธญๆ–‡ ยท ๆ—ฅๆœฌ่ชž ยท ํ•œ๊ตญ์–ด

HTTP

ํ™ˆ ยท C# ยท IDE ยท UO API ยท Aโ€“Z ยท ์˜ˆ ยท ์–ธ์–ด

HTTP-์งˆ์˜ ๋ฐ ์‘๋‹ต ์ฝ๊ธฐ. ์ƒํƒœ ์ฝ”๋“œ, ํ—ค๋”, ์‘๋‹ต ๋ณธ๋ฌธ์€ ๋ณ„๋„์˜ ๋ฐ์ดํ„ฐ์ž…๋‹ˆ๋‹ค.

์„น์…˜

์ฃผ์ œ ๋ช…๋ น ๋ฐ ์„ค๋ช…
HTTP_Get UO.HTTP_Get
HTTP_Post UO.HTTP_Post
HTTP_Header UO.HTTP_Header
HTTP_Body UO.HTTP_Body
์™ธ๋ถ€ ๋ฉ”์‹œ์ง€ UO.Messenger - ํ…”๋ ˆ๊ทธ๋žจ, ๋””์Šค์ฝ”๋“œ, ๋ฐ”์ด๋ฒ„

๋ชจ๋“  ํ…Œ๋งˆ ๋ช…๋ น

HTTP ์š”์ฒญ

UO.HTTP_Get ยท UO.HTTP_Post ยท UO.HTTP_Header ยท UO.HTTP_Body

์ฃผ์ œ์— ๋Œ€ํ•œ ์˜ˆ

๊ฐ ๋ช…๋ น ์นด๋“œ์—๋Š” ์ง์ ‘ ์ฝ”๋“œ, ํ•จ์ˆ˜ ๋ฐ ํด๋ž˜์Šค ๋“ฑ ์—ฌ๋Ÿฌ ๊ฐ€์ง€ ๋…๋ฆฝ์ ์ธ ์‹œ๋‚˜๋ฆฌ์˜ค๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ์€ ์„ ํƒ๋œ ์งง์€ ์‹œ๋‚˜๋ฆฌ์˜ค์ž…๋‹ˆ๋‹ค. ์ œํ•œ์‚ฌํ•ญ๊ณผ ์ „์ฒด ๋ถ„์„์ด ์นด๋“œ์— ํฌํ•จ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

UO.HTTP_Get

๋งค๊ฐœ๋ณ€์ˆ˜, ๊ฒฐ๊ณผ ๋ฐ ๋ชจ๋“  ์‹œ๋‚˜๋ฆฌ์˜ค

UO.HTTP_Get("https://example.com/csharp-config.json");
try
{
    string body = Convert.ToString(UO.HTTP_Body()) ?? "";
    using (var document = System.Text.Json.JsonDocument.Parse(body))
    {
        if (document.RootElement.TryGetProperty("minMana", out var value) &&
            value.TryGetInt32(out int threshold) && threshold is >= 0 and <= 100)
            return threshold;
    }
}
catch (System.Text.Json.JsonException)
{
    UO.Print("Response is not a valid configuration; using the default.");
}
return 30;

UO.HTTP_Body

๋งค๊ฐœ๋ณ€์ˆ˜, ๊ฒฐ๊ณผ ๋ฐ ๋ชจ๋“  ์‹œ๋‚˜๋ฆฌ์˜ค

UO.HTTP_Get("https://example.com/C#-permission");
var permission = TrimText(UO.HTTP_Body());
return Truth(permission == "allow");

bool Truth(object? value) => value is bool flag ? flag : value is string text ? !string.IsNullOrEmpty(text) : value != null && Convert.ToDouble(value) != 0;

string TrimText(object? value) => (Convert.ToString(value) ?? "").Trim();

์ „์ฒด ๋ช…๋ น ์ฐธ์กฐ


UO.HTTP_Body

ะฃะบั€ะฐั—ะฝััŒะบะฐ ยท ะ ัƒััะบะธะน ยท English ยท Deutsch ยท Franรงais ยท Italiano ยท Espaรฑol ยท ็น้ซ”ไธญๆ–‡ ยท ๆ—ฅๆœฌ่ชž ยท ํ•œ๊ตญ์–ด

C# ์‹œ๊ทธ๋‹ˆ์ฒ˜

dynamic UO.HTTP_Body();

์ด ์Šคํฌ๋ฆฝํŠธ์˜ ๋งˆ์ง€๋ง‰ HTTP ์‘๋‹ต ํ…์ŠคํŠธ๋ฅผ ์ฝ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด HTTP_Get ํ›„์— ์„ค์ •์œผ๋กœ JSON๋ฅผ ๊ตฌ๋ฌธ ๋ถ„์„ํ•˜๊ฑฐ๋‚˜ ์„œ๋น„์Šค ์‘๋‹ต์„ ์˜ˆ์ƒ ๋ฌธ์ž์—ด๊ณผ ๋น„๊ตํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํŒ๋… ์ž์ฒด๋Š” ๋„คํŠธ์›Œํฌ๋กœ ์ด๋™ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๋งค๊ฐœ๋ณ€์ˆ˜

๋งค๊ฐœ๋ณ€์ˆ˜๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.

๋ฐ˜ํ™˜๊ฐ’

string โ€” ์ €์žฅ๋œ ํ…์ŠคํŠธ. ์ด ๋Ÿฐํƒ€์ž„์˜ ์ฒซ ๋ฒˆ์งธ ์š”์ฒญ ์ „, ์ŠคํŠธ๋ฆผ์˜ GET ๋’ค, ๋„คํŠธ์›Œํฌ ์˜ค๋ฅ˜ ํ›„ - ๋นˆ ์ค„์ž…๋‹ˆ๋‹ค. ์ผ๋ฐ˜์ ์ธ ๋‹ต๋ณ€์—๋Š” ๋ณธ๋ฌธ์ด ๋น„์–ด ์žˆ์Šต๋‹ˆ๋‹ค(์˜ˆ: HTTP 204). ๊ฒŒ์žฌ ์—ฌ๋ถ€๋ฅผ ํŒ๋‹จํ•˜๊ธฐ ์œ„ํ•ด ๊ฒฐ๊ณผ๋ฅผ true/false์™€ ๋น„๊ตํ•˜์ง€ ๋งˆ์„ธ์š”.

๋™์ž‘

  • ๋งˆ์ง€๋ง‰ ์‘๋‹ต์€ ์Šคํฌ๋ฆฝํŠธ์˜ ํ˜„์žฌ ๋Ÿฐํƒ€์ž„์—๋งŒ ์ €์žฅ๋ฉ๋‹ˆ๋‹ค. ์ด ๋Ÿฐํƒ€์ž„์˜ ๋‹ค์Œ GET/POST๊ฐ€ ์ด๋ฅผ ๋Œ€์ฒดํ•ฉ๋‹ˆ๋‹ค. ๋™์‹œ์— ์‹คํ–‰๋˜๋Š” ๋‹ค๋ฅธ ์Šคํฌ๋ฆฝํŠธ๋Š” ์ด๋ฅผ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋‹ต๋ณ€์€ ํ”„๋กœํ•„์— ์ €์žฅ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ ์š”์ฒญ ์ดํ›„์— ํ•„์š”ํ•˜๋‹ค๋ฉด ๋ณธ๋ฌธ๊ณผ ํ—ค๋”๋ฅผ ๋ฏธ๋ฆฌ ๋ณ€์ˆ˜์— ๋ณต์‚ฌํ•ด ๋‘์„ธ์š”.
  • GET/POST ์‹คํ–‰ ์ค‘์ธ ์Šคํฌ๋ฆฝํŠธ ์Šค๋ ˆ๋“œ์—์„œ ์‘๋‹ต์„ ๊ธฐ๋‹ค๋ฆฝ๋‹ˆ๋‹ค. ๋ณธ๋ฌธ์„ ์š”์ฒญํ•˜๊ณ  ์ฝ๋Š” ๋ฐ ๊ฑธ๋ฆฌ๋Š” ์ด ์‹œ๊ฐ„์€ 20์ดˆ์ž…๋‹ˆ๋‹ค. ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์ค‘์ง€ํ•˜๋ฉด ๋Œ€๊ธฐ๊ฐ€ ์ทจ์†Œ๋ฉ๋‹ˆ๋‹ค. HTTP_Body/HTTP_Header ์•„๋ฌด๊ฒƒ๋„ ๊ธฐ๋Œ€ํ•˜์ง€ ๋งˆ์„ธ์š”. FileStream๊ฐ€ ์ „์†ก๋˜๋Š” ๊ฒฝ์šฐ์—๋„ ์ „์ฒด ๋ณธ๋ฌธ์ด ๋ฉ”๋ชจ๋ฆฌ์— ๋กœ๋“œ๋ฉ๋‹ˆ๋‹ค. ์ด๋Š” ๋Œ€์šฉ๋Ÿ‰ ํŒŒ์ผ์˜ ์ŠคํŠธ๋ฆฌ๋ฐ ๋‹ค์šด๋กœ๋“œ๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค.
  • ์ธ์ฝ”๋”ฉ์ด ์—†๊ฑฐ๋‚˜ ์•Œ ์ˆ˜ ์—†๋Š” ๊ฒฝ์šฐ(UTF-8) ํ…์ŠคํŠธ๋Š” ์‘๋‹ต์˜ ๋ฌธ์ž ์„ธํŠธ์— ๋”ฐ๋ผ ๋””์ฝ”๋”ฉ๋ฉ๋‹ˆ๋‹ค. ๋‹ต๋ณ€ HTTP 4xx/5xx๋„ ์ฝ์Šต๋‹ˆ๋‹ค. ๋„คํŠธ์›Œํฌ ์˜ค๋ฅ˜๋‚˜ ์‹œ๊ฐ„ ์ดˆ๊ณผ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ํด๋ผ์ด์–ธํŠธ๋Š” ์˜ค๋ฅ˜๋ฅผ ๊ธฐ๋กํ•˜๊ณ  ๋ณธ๋ฌธ๊ณผ ํ—ค๋”๋Š” ๋น„์–ด ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. Try/Catch๋Š” ์˜ˆ์ƒ ์‘๋‹ต ํ™•์ธ์„ ๋Œ€์ฒดํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์Šคํฌ๋ฆฝํŠธ ์ทจ์†Œ๊ฐ€ ๋”์šฑ ํ™•์žฅ๋ฉ๋‹ˆ๋‹ค. ๋กœ์ปฌ ํŒŒ์ผ ์“ฐ๊ธฐ ๋˜๋Š” ์ž˜๋ชป๋œ ์ธ์ˆ˜ ์˜ค๋ฅ˜๋Š” Try/Catch๋ฅผ ํ†ตํ•ด ํฌ์ฐฉ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • HTTP_Body๋Š” ๋งˆ์ง€๋ง‰ ๋ฒ„ํผ๋งŒ ์ฝ์Šต๋‹ˆ๋‹ค. ์š”์ฒญ์„ ๋ฐ˜๋ณตํ•˜์ง€ ์•Š๊ณ  ์‘๋‹ต์„ ์ง€์šฐ์ง€ ์•Š์œผ๋ฉฐ ๋‹ค๋ฅธ ์Šคํฌ๋ฆฝํŠธ์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์ƒ์„ฑํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋ฐ”์ด๋„ˆ๋ฆฌ GET ์ดํ›„์—๋Š” ์ด ํ…์ŠคํŠธ ๋ฒ„ํผ๊ฐ€ ์•„๋‹Œ MemoryStream.ToArray() ๋˜๋Š” ์ €์žฅ๋œ ํŒŒ์ผ์„ ์‚ฌ์šฉํ•˜์„ธ์š”.

๋…๋ฆฝ ์‹คํ–‰ C# ์Šคํฌ๋ฆฝํŠธ

๊ฐ ์˜ˆ์ œ๋Š” ๋ณ„๋„์˜ ํŒŒ์ผ .csx๋กœ ์ €์žฅํ•˜๊ณ  IDE์—์„œ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. UO ๊ฐœ์ฒด์™€ ๋Œ€๊ธฐ ๊ธฐ๋Šฅ์€ ํ˜„์žฌ ๊ฒŒ์ž„ ์„ธ์…˜์˜ ํ˜ธ์ŠคํŠธ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ๋ฐ๋ชจ serial ์ด๋ฆ„, ์ขŒํ‘œ, ์ฃผ์†Œ๋ฅผ ์ž์‹ ์˜ ๊ฒƒ์œผ๋กœ ๋ฐ”๊พธ์„ธ์š”.

๊ฐ„๋‹จํ•œ ์˜ˆ์ œ

์ด ์ฝ”๋“œ๋ฅผ ๋ณ„๋„์˜ .csx ํŒŒ์ผ๋กœ ์‹คํ–‰ํ•˜์„ธ์š”. ์ธ์ˆ˜๋Š” ํ˜ธ์ถœ์— ์ง์ ‘ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค. ์˜ˆ์ œ์˜ ์ด๋ฆ„, serial, ์ฃผ์†Œ๋ฅผ ์ž์‹ ์˜ ๊ฐ’์œผ๋กœ ๋ฐ”๊พธ์„ธ์š”.

ํ•œ ๋ฒˆ์˜ ํ˜ธ์ถœ ๊ฒฐ๊ณผ๋ฅผ ์ €๋„์— ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค. ์ˆซ์ž๋ฅผ bool๋กœ ๋ณ€ํ™˜ํ•˜์ง€ ์•Š์œผ๋ฉฐ, ๋ฐฐ์—ด์ด๋ฉด ์š”์†Œ ์ˆ˜๋ฅผ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค.

UO.HTTP_Get("https://example.com/");
object? result = UO.HTTP_Body();
UO.Print(result is Array values ? "Count: " + values.Length : Convert.ToString(result));

1. ์˜ˆ์ƒ๋˜๋Š” ํ…์ŠคํŠธ์—๋งŒ ์ž‘์—… ํ—ˆ์šฉ - ์ง์ ‘ ์Šคํฌ๋ฆฝํŠธ

๋งค๊ฐœ๋ณ€์ˆ˜์™€ ์‹คํ–‰ ํ๋ฆ„:

  • ์„œ๋น„์Šค๋Š” ํ—ˆ์šฉ์„ ๋ฐ˜ํ™˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋นˆ ๋‹ต์„ ํฌํ•จํ•œ ๋˜ ๋‹ค๋ฅธ ๋‹ต์€ false์ž…๋‹ˆ๋‹ค. ์ด ์˜ˆ์—์„œ๋Š” ํ•ด์ƒ๋„๋งŒ ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค. ์ด๋™์ด๋‚˜ ์ „ํˆฌ๋ฅผ ์œ ๋ฐœํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
  • example.com ์ฃผ์†Œ๋Š” ์˜ˆ์‹œ์ผ ๋ฟ์ด๋ฉฐ ๊ทธ๋Ÿฌํ•œ ์„œ๋น„์Šค๋ฅผ ์ œ๊ณตํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ž์‹ ์˜ ๊ฒƒ์œผ๋กœ ๊ต์ฒดํ•˜์‹ญ์‹œ์˜ค. ์˜ˆ์ œ๋ฅผ ์‹คํ–‰ํ•˜๋ฉด ์‹ค์ œ HTTP ์ฟผ๋ฆฌ๊ฐ€ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค. ์ด ์˜ˆ์—์„œ๋Š” ๊ฒŒ์ž„ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
UO.HTTP_Get("https://example.com/C#-permission");
var permission = TrimText(UO.HTTP_Body());
return Truth(permission == "allow");

bool Truth(object? value) => value is bool flag ? flag : value is string text ? !string.IsNullOrEmpty(text) : value != null && Convert.ToDouble(value) != 0;

string TrimText(object? value) => (Convert.ToString(value) ?? "").Trim();

2. ๋‘ ๊ฐ€์ง€ ๋‹ต๋ณ€์—์„œ ์„ค๋ช… ์ˆ˜์ง‘ - ์Šคํฌ๋ฆฝํŠธ ๊ธฐ๋Šฅ

๋งค๊ฐœ๋ณ€์ˆ˜์™€ ์‹คํ–‰ ํ๋ฆ„:

  • ๋จผ์ € ๊ฒฝ๋กœ ์ด๋ฆ„์ด ์ €์žฅ๋œ ๋‹ค์Œ ์„ค๋ช…์ด ์š”์ฒญ๋ฉ๋‹ˆ๋‹ค. routeName๊ฐ€ ์—†์œผ๋ฉด ๋‘ ๋ฒˆ์งธ GET๊ฐ€ ์ฒซ ๋ฒˆ์งธ ๋‹ต๋ณ€์„ ๋Œ€์ฒดํ•ฉ๋‹ˆ๋‹ค.
  • example.com ์ฃผ์†Œ๋Š” ์˜ˆ์‹œ์ผ ๋ฟ์ด๋ฉฐ ๊ทธ๋Ÿฌํ•œ ์„œ๋น„์Šค๋ฅผ ์ œ๊ณตํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ž์‹ ์˜ ๊ฒƒ์œผ๋กœ ๊ต์ฒดํ•˜์‹ญ์‹œ์˜ค. ์˜ˆ์ œ๋ฅผ ์‹คํ–‰ํ•˜๋ฉด ์‹ค์ œ HTTP ์ฟผ๋ฆฌ๊ฐ€ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค. ์ด ์˜ˆ์—์„œ๋Š” ๊ฒŒ์ž„ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
return  RunScenario();
 object? RunScenario()
{
    UO.HTTP_Get("https://example.com/route-name");
    var routeName = UO.HTTP_Body();
    UO.HTTP_Get("https://example.com/route-description");
    return routeName + ": " + UO.HTTP_Body();
    return null;
}

3. ํ…์ŠคํŠธ ๋ฒ„ํผ์—์„œ ๋ฐ”์ด๋„ˆ๋ฆฌ ์ŠคํŠธ๋ฆผ ๋ถ„๋ฆฌ - ์Šคํฌ๋ฆฝํŠธ ํด๋ž˜์Šค

๋งค๊ฐœ๋ณ€์ˆ˜์™€ ์‹คํ–‰ ํ๋ฆ„:

  • System.IO.MemoryStream์˜ GET ์ดํ›„์—๋Š” ๋ฐ”์ดํŠธ๊ฐ€ ์ŠคํŠธ๋ฆผ์— ์žˆ๊ณ  HTTP_Body๋Š” ๋น„์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ๊ทœ์น™์€ ์˜ฌ๋ฐ”๋ฅธ ์ด์ง„ ๋‹ต๋ณ€์—๋„ ์ ์šฉ๋ฉ๋‹ˆ๋‹ค. ๋นˆ ํ…์ŠคํŠธ๋Š” ๋ฐ”์ดํŠธ๊ฐ€ ์—†์Œ์„ ์˜๋ฏธํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
return  new ScriptScenario(UO, WaitAsync, Args, Cancellation, Checkpoint).RunScenario();
class ScriptScenario
{
    private readonly global::ClassicUO.CSharp.CSharpGameApi UO;
    private readonly Func<int, Task> WaitAsync;
    private readonly string[] Args;
    private readonly CancellationToken Cancellation;
    private readonly Action Checkpoint;
    public ScriptScenario(global::ClassicUO.CSharp.CSharpGameApi api, Func<int, Task> wait, string[] args, CancellationToken cancellation, Action checkpoint)
    {
        UO = api;
        WaitAsync = wait;
        Args = args;
        Cancellation = cancellation;
        Checkpoint = checkpoint;
    }

     public object? RunScenario()
    {
        using (var data = new System.IO.MemoryStream())
        {
            UO.HTTP_Get("https://example.com/route-data.bin", data);
            string body = Convert.ToString(UO.HTTP_Body()) ?? "";
            UO.Print($"Binary bytes: {data.Length}; text-body characters: {body.Length}");
            return body;
        }

        return null;
    }
}

๋ช…๋ น ๊ตฌํ˜„

C#์€ ๋Œ€์†Œ๋ฌธ์ž๋ฅผ ๊ตฌ๋ถ„ํ•ฉ๋‹ˆ๋‹ค. ์ •ํ™•ํ•œ UO ์ด๋ฆ„์„ ์‚ฌ์šฉํ•˜์„ธ์š”. ์ˆซ์ž ํ”Œ๋ž˜๊ทธ๋Š” 0๊ณผ ๋น„๊ตํ•˜๊ณ  ๋ฐ˜ํ™˜ ํ˜•์‹์ด bool์ผ ๋•Œ๋งŒ bool์„ ์‚ฌ์šฉํ•˜์„ธ์š”.

UO.HTTP_Body() โ€” UO.HTTP_Body()

๋งค๊ฐœ๋ณ€์ˆ˜: ๋งค๊ฐœ๋ณ€์ˆ˜๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.

๊ฒฐ๊ณผ: dynamic / Any.

ํ˜ธ์ถœ ์‹œ:

๋‚ด๋ถ€ ๊ตฌํ˜„ ์ฝ”๋“œ์ด๋ฉฐ ๋…๋ฆฝ ์Šคํฌ๋ฆฝํŠธ๋กœ ์‹คํ–‰ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

case "http_body": return new InjectionValue(httpBody);

UO.HTTP_Get

ะฃะบั€ะฐั—ะฝััŒะบะฐ ยท ะ ัƒััะบะธะน ยท English ยท Deutsch ยท Franรงais ยท Italiano ยท Espaรฑol ยท ็น้ซ”ไธญๆ–‡ ยท ๆ—ฅๆœฌ่ชž ยท ํ•œ๊ตญ์–ด

C# ์‹œ๊ทธ๋‹ˆ์ฒ˜

void UO.HTTP_Get(object? url);
void UO.HTTP_Get(object? url, object? lStream);

HTTP/HTTPS์— ๋ฐ์ดํ„ฐ๋ฅผ ๋กœ๋“œํ•ฉ๋‹ˆ๋‹ค. ์„œ๋น„์Šค์—์„œ ์„ค์ •์„ ์ฝ๊ฑฐ๋‚˜ ํŒŒ์ผ์„ ์ˆ˜์‹ ํ•˜๋Š” ๋ฐ ์ ํ•ฉํ•ฉ๋‹ˆ๋‹ค. ๊ฒŒ์ž„ ์—ฐ๊ฒฐ์„ ๊ด€๋ฆฌํ•˜๊ฑฐ๋‚˜ ๋‹ค์šด๋กœ๋“œํ•œ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๋งค๊ฐœ๋ณ€์ˆ˜

  • url - ํ•„์ˆ˜ string: ์ „์ฒด ์ฃผ์†Œ http:// ๋˜๋Š” https://. example.com์„ ์„œ๋น„์Šค ์ฃผ์†Œ๋กœ ๋ฐ”๊ฟ‰๋‹ˆ๋‹ค. ์ด ์š”์ฒญ์—๋Š” ๊ฒŒ์ž„ ๋ฐ ๊ฒŒ์ž„ ์„œ๋ฒ„๊ฐ€ ํ•„์š”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ฒŒ์ž„ ํ”„๋ก์‹œ ํ”„๋กœํ•„์ด ์•„๋‹Œ ํ”„๋กœ์„ธ์Šค์˜ ์ผ๋ฐ˜ ๋„คํŠธ์›Œํฌ ์—ฐ๊ฒฐ์ด ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.
  • lStream๋Š” ์„ ํƒ์  ํ‘œ์ค€์ž…๋‹ˆ๋‹ค. .NET System.IO.Stream, ์“ฐ๊ธฐ ๊ฐ€๋Šฅ: ์˜ˆ๋ฅผ ๋“ค์–ด ์ƒˆ๋กœ์šด System.IO.MemoryStream() ๋˜๋Š” System.IO.File.Create(๊ฒฝ๋กœ)์ž…๋‹ˆ๋‹ค. ๊ทธ๊ฒƒ์ด ์—†๊ฑฐ๋‚˜ 0/null๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ํ…์ŠคํŠธ๋Š” HTTP_Body()๊ฐ€ ๋ฉ๋‹ˆ๋‹ค. ์Šค๋ ˆ๋“œ๋Š” ์›๋ณธ ๋ฐ”์ดํŠธ๋ฅผ ๋ฐ›์Šต๋‹ˆ๋‹ค. ๊ฒ€์ƒ‰ ๊ฐ€๋Šฅํ•œ ์ŠคํŠธ๋ฆผ์˜ ๊ฒฝ์šฐ ์ด์ „ ์ฝ˜ํ…์ธ ๋Š” 0์œผ๋กœ ์žฌ์„ค์ •๋˜๊ณ  ์“ฐ๊ธฐ ์ „ํ›„์˜ ์œ„์น˜๋Š” 0๊ฐ€ ๋ฉ๋‹ˆ๋‹ค. ํƒ์ƒ‰ํ•  ์ˆ˜ ์—†๋Š” ์Šค๋ ˆ๋“œ๋Š” ํ˜„์žฌ ์œ„์น˜์— ๋Œ€ํ•œ ์“ฐ๊ธฐ๋ฅผ ๋ฐ›์Šต๋‹ˆ๋‹ค. File.Create๋ฅผ ํ†ตํ•ด FileStream์„ ์ƒ์„ฑํ•˜๋ฉด ํŒŒ์ผ์ด ์ฆ‰์‹œ ๋ฌดํšจํ™”๋ฉ๋‹ˆ๋‹ค. ์ƒ์œ„ ๋””๋ ‰ํ† ๋ฆฌ๊ฐ€ ์กด์žฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋ถ€์ ์ ˆํ•œ ์ธ์ˆ˜ ๋˜๋Š” ์ฝ๊ธฐ ์ „์šฉ ์ŠคํŠธ๋ฆผ์€ ์š”์ฒญ ์ „์— ๊ฑฐ๋ถ€๋ฉ๋‹ˆ๋‹ค.

๋ฐ˜ํ™˜๊ฐ’

void - ๋ฐ˜ํ™˜ ๊ฐ’์ด ์—†์Šต๋‹ˆ๋‹ค. ๋ณ„๋„์˜ ํšŒ์„ ์œผ๋กœ ์ „ํ™”ํ•˜์„ธ์š”. ๊ฒฐ๊ณผ๋Š” bool, ID ๋˜๋Š” ๋ฐ”์ดํŠธ ์ˆ˜๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. HTTP_Body()๋Š” ์ผ๋ฐ˜ ์š”์ฒญ ์ดํ›„์˜ ํ…์ŠคํŠธ๋ฅผ ์ฝ๊ณ , ์ „์†ก๋œ ์ŠคํŠธ๋ฆผ์—์„œ ๋ฐ”์ด๋„ˆ๋ฆฌ ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ์Šต๋‹ˆ๋‹ค.

๋™์ž‘

  • ๋งˆ์ง€๋ง‰ ์‘๋‹ต์€ ์Šคํฌ๋ฆฝํŠธ์˜ ํ˜„์žฌ ๋Ÿฐํƒ€์ž„์—๋งŒ ์ €์žฅ๋ฉ๋‹ˆ๋‹ค. ์ด ๋Ÿฐํƒ€์ž„์˜ ๋‹ค์Œ GET/POST๊ฐ€ ์ด๋ฅผ ๋Œ€์ฒดํ•ฉ๋‹ˆ๋‹ค. ๋™์‹œ์— ์‹คํ–‰๋˜๋Š” ๋‹ค๋ฅธ ์Šคํฌ๋ฆฝํŠธ๋Š” ์ด๋ฅผ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋‹ต๋ณ€์€ ํ”„๋กœํ•„์— ์ €์žฅ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ ์š”์ฒญ ์ดํ›„์— ํ•„์š”ํ•˜๋‹ค๋ฉด ๋ณธ๋ฌธ๊ณผ ํ—ค๋”๋ฅผ ๋ฏธ๋ฆฌ ๋ณ€์ˆ˜์— ๋ณต์‚ฌํ•ด ๋‘์„ธ์š”.
  • GET/POST ์‹คํ–‰ ์ค‘์ธ ์Šคํฌ๋ฆฝํŠธ ์Šค๋ ˆ๋“œ์—์„œ ์‘๋‹ต์„ ๊ธฐ๋‹ค๋ฆฝ๋‹ˆ๋‹ค. ๋ณธ๋ฌธ์„ ์š”์ฒญํ•˜๊ณ  ์ฝ๋Š” ๋ฐ ๊ฑธ๋ฆฌ๋Š” ์ด ์‹œ๊ฐ„์€ 20์ดˆ์ž…๋‹ˆ๋‹ค. ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์ค‘์ง€ํ•˜๋ฉด ๋Œ€๊ธฐ๊ฐ€ ์ทจ์†Œ๋ฉ๋‹ˆ๋‹ค. HTTP_Body/HTTP_Header ์•„๋ฌด๊ฒƒ๋„ ๊ธฐ๋Œ€ํ•˜์ง€ ๋งˆ์„ธ์š”. FileStream๊ฐ€ ์ „์†ก๋˜๋Š” ๊ฒฝ์šฐ์—๋„ ์ „์ฒด ๋ณธ๋ฌธ์ด ๋ฉ”๋ชจ๋ฆฌ์— ๋กœ๋“œ๋ฉ๋‹ˆ๋‹ค. ์ด๋Š” ๋Œ€์šฉ๋Ÿ‰ ํŒŒ์ผ์˜ ์ŠคํŠธ๋ฆฌ๋ฐ ๋‹ค์šด๋กœ๋“œ๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค.
  • ์ธ์ฝ”๋”ฉ์ด ์—†๊ฑฐ๋‚˜ ์•Œ ์ˆ˜ ์—†๋Š” ๊ฒฝ์šฐ(UTF-8) ํ…์ŠคํŠธ๋Š” ์‘๋‹ต์˜ ๋ฌธ์ž ์„ธํŠธ์— ๋”ฐ๋ผ ๋””์ฝ”๋”ฉ๋ฉ๋‹ˆ๋‹ค. ๋‹ต๋ณ€ HTTP 4xx/5xx๋„ ์ฝ์Šต๋‹ˆ๋‹ค. ๋„คํŠธ์›Œํฌ ์˜ค๋ฅ˜๋‚˜ ์‹œ๊ฐ„ ์ดˆ๊ณผ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ํด๋ผ์ด์–ธํŠธ๋Š” ์˜ค๋ฅ˜๋ฅผ ๊ธฐ๋กํ•˜๊ณ  ๋ณธ๋ฌธ๊ณผ ํ—ค๋”๋Š” ๋น„์–ด ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. Try/Catch๋Š” ์˜ˆ์ƒ ์‘๋‹ต ํ™•์ธ์„ ๋Œ€์ฒดํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์Šคํฌ๋ฆฝํŠธ ์ทจ์†Œ๊ฐ€ ๋”์šฑ ํ™•์žฅ๋ฉ๋‹ˆ๋‹ค. ๋กœ์ปฌ ํŒŒ์ผ ์“ฐ๊ธฐ ๋˜๋Š” ์ž˜๋ชป๋œ ์ธ์ˆ˜ ์˜ค๋ฅ˜๋Š” Try/Catch๋ฅผ ํ†ตํ•ด ํฌ์ฐฉ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • GET ์Šค๋ ˆ๋“œ๊ฐ€ ์—†์œผ๋ฉด ํ…์ŠคํŠธ์™€ ์ œ๋ชฉ์„ ์—…๋ฐ์ดํŠธํ•ฉ๋‹ˆ๋‹ค. GET๋Š” ์ŠคํŠธ๋ฆผ๊ณผ ํ•จ๊ป˜ ๋ฐ”์ดํŠธ๋ฅผ ์“ฐ๊ณ  HTTP_Body๋ฅผ ์ง€์šฐ๊ณ  ํ—ค๋”๋ฅผ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค. ๋นˆ ์‘๋‹ต/๋„คํŠธ์›Œํฌ ์˜ค๋ฅ˜๋ผ๋„ ์ŠคํŠธ๋ฆผ์˜ ๋‚ด์šฉ์„ ๋นˆ ๋ฐ์ดํ„ฐ๋กœ ๋Œ€์ฒดํ•ฉ๋‹ˆ๋‹ค. ์ด์ „ ํŒŒ์ผ์„ ์ €์žฅํ•˜๋ ค๋ฉด ๋จผ์ € MemoryStream์— ๋‹ค์šด๋กœ๋“œํ•˜๊ณ  ๋ฐ์ดํ„ฐ๋ฅผ ํ™•์ธํ•˜์„ธ์š”.

๋…๋ฆฝ ์‹คํ–‰ C# ์Šคํฌ๋ฆฝํŠธ

๊ฐ ์˜ˆ์ œ๋Š” ๋ณ„๋„์˜ ํŒŒ์ผ .csx๋กœ ์ €์žฅํ•˜๊ณ  IDE์—์„œ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. UO ๊ฐœ์ฒด์™€ ๋Œ€๊ธฐ ๊ธฐ๋Šฅ์€ ํ˜„์žฌ ๊ฒŒ์ž„ ์„ธ์…˜์˜ ํ˜ธ์ŠคํŠธ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ๋ฐ๋ชจ serial ์ด๋ฆ„, ์ขŒํ‘œ, ์ฃผ์†Œ๋ฅผ ์ž์‹ ์˜ ๊ฒƒ์œผ๋กœ ๋ฐ”๊พธ์„ธ์š”.

๊ฐ„๋‹จํ•œ ์˜ˆ์ œ

์ด ์ฝ”๋“œ๋ฅผ ๋ณ„๋„์˜ .csx ํŒŒ์ผ๋กœ ์‹คํ–‰ํ•˜์„ธ์š”. ์ธ์ˆ˜๋Š” ํ˜ธ์ถœ์— ์ง์ ‘ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค. ์˜ˆ์ œ์˜ ์ด๋ฆ„, serial, ์ฃผ์†Œ๋ฅผ ์ž์‹ ์˜ ๊ฐ’์œผ๋กœ ๋ฐ”๊พธ์„ธ์š”.

๋ช…๋ น์€ ๋…๋ฆฝ๋œ ๋ฌธ์ž…๋‹ˆ๋‹ค. ์•ž์˜ ๋ฉ”์‹œ์ง€๋Š” ํ˜ธ์ถœ ์‹œ์ž‘์„ ๋‚˜ํƒ€๋‚ด๋ฉฐ ์„œ๋ฒ„ ๋™์ž‘์˜ ์™„๋ฃŒ๋ฅผ ํ™•์ธํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

UO.Print("Calling UO.HTTP_Get");
UO.HTTP_Get("https://example.com/");

1. JSON ์ฝ๊ธฐ - ๋ฒ”์œ„ ํ™•์ธ์ด ํฌํ•จ๋œ ๊ตฌ์„ฑ - ์ง์ ‘ ์Šคํฌ๋ฆฝํŠธ

๋งค๊ฐœ๋ณ€์ˆ˜์™€ ์‹คํ–‰ ํ๋ฆ„:

  • example.com - ๋ฐ๋ชจ ์ฃผ์†Œ; ๊ทธ๊ฒƒ์„ ๋‹น์‹  ๊ฒƒ์œผ๋กœ ๊ต์ฒดํ•˜์‹ญ์‹œ์˜ค. System.Text.Json์€ ์‘๋‹ต ๋ณธ๋ฌธ์„ ์ฝ๊ณ  0๋ถ€ํ„ฐ 100๊นŒ์ง€ ์ •์ˆ˜ minMana๋งŒ ํ—ˆ์šฉํ•ฉ๋‹ˆ๋‹ค. ๋น„์–ด ์žˆ๊ฑฐ๋‚˜ ์œ ํšจํ•˜์ง€ ์•Š์€ JSON๋Š” ๊ธฐ๋ณธ๊ฐ’ 30๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.
UO.HTTP_Get("https://example.com/csharp-config.json");
try
{
    string body = Convert.ToString(UO.HTTP_Body()) ?? "";
    using (var document = System.Text.Json.JsonDocument.Parse(body))
    {
        if (document.RootElement.TryGetProperty("minMana", out var value) &&
            value.TryGetInt32(out int threshold) && threshold is >= 0 and <= 100)
            return threshold;
    }
}
catch (System.Text.Json.JsonException)
{
    UO.Print("Response is not a valid configuration; using the default.");
}
return 30;

2. ํŒŒ์ผ์„ ์ €์žฅํ•˜๊ธฐ ์ „์— ๋ฐ”์ด๋„ˆ๋ฆฌ ๋ฐ”์ดํŠธ๋ฅผ ํ™•์ธํ•˜์„ธ์š” - ์Šคํฌ๋ฆฝํŠธ ๊ธฐ๋Šฅ

๋งค๊ฐœ๋ณ€์ˆ˜์™€ ์‹คํ–‰ ํ๋ฆ„:

  • ํ‘œ์ค€ ์‹œ์Šคํ…œ.IO.MemoryStream์ด ์ „์†ก๋ฉ๋‹ˆ๋‹ค. ์‘๋‹ต์˜ ์›๋ณธ ๋ฐ”์ดํŠธ๋ฅผ ์ˆ˜์‹ ํ•ฉ๋‹ˆ๋‹ค. HTTP_Body๊ฐ€ ๋น„์–ด ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ๊ธฐ์กด download.bin์€ ๋น„์–ด ์žˆ์ง€ ์•Š์€ ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์‹ ํ•œ ํ›„์—๋งŒ ๊ต์ฒด๋ฉ๋‹ˆ๋‹ค. ๊ธธ์ด๋ฅผ ํ™•์ธํ•œ๋‹ค๊ณ  ํ•ด์„œ ๋‚ด์šฉ์ด ์ •ํ™•ํ•˜๋‹ค๋Š” ๊ฒƒ์„ ์ฆ๋ช…ํ•˜๋Š” ๊ฒƒ์€ ์•„๋‹™๋‹ˆ๋‹ค.
return  RunScenario();
 object? RunScenario()
{
    using (var data = new System.IO.MemoryStream())
    {
        UO.HTTP_Get("https://example.com/route-data.bin", data);
        if (data.Length == 0)
        {
            UO.Print("The response is empty; no file was written.");
            return false;
        }

        System.IO.File.WriteAllBytes("download.bin", data.ToArray());
        return true;
    }

    return null;
}

3. ๋ฐ”์ด๋„ˆ๋ฆฌ ์‘๋‹ต์„ FileStream - ์Šคํฌ๋ฆฝํŠธ ํด๋ž˜์Šค์— ์ง์ ‘ ๋‹ค์šด๋กœ๋“œํ•˜์„ธ์š”.

๋งค๊ฐœ๋ณ€์ˆ˜์™€ ์‹คํ–‰ ํ๋ฆ„:

  • System.IO.File.Create๋Š” ํŒŒ์ผ์„ ์ƒ์„ฑํ•˜๊ฑฐ๋‚˜ ์ฆ‰์‹œ null๋กœ ๋งŒ๋“ญ๋‹ˆ๋‹ค. GET๋Š” ์›๋ž˜ ๋ฐ”์ดํŠธ๋ฅผ ์”๋‹ˆ๋‹ค. ์‘๋‹ต์ด ๋น„์–ด ์žˆ์œผ๋ฉด ํŒŒ์ผ์€ ๋น„์–ด ์žˆ๋Š” ์ƒํƒœ๋กœ ์œ ์ง€๋ฉ๋‹ˆ๋‹ค. ์ด์ „ ๋ฒ„์ „์„ ์ €์žฅํ•˜๋ ค๋ฉด ๋จผ์ € ์ด์ „ ์Šคํฌ๋ฆฝํŠธ์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ MemoryStream๋ฅผ ์‚ฌ์šฉํ•˜์„ธ์š”. ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ŠคํŠธ๋ฆผ์ด ๋‹ซํž™๋‹ˆ๋‹ค.
return  new ScriptScenario(UO, WaitAsync, Args, Cancellation, Checkpoint).RunScenario();
class ScriptScenario
{
    private readonly global::ClassicUO.CSharp.CSharpGameApi UO;
    private readonly Func<int, Task> WaitAsync;
    private readonly string[] Args;
    private readonly CancellationToken Cancellation;
    private readonly Action Checkpoint;
    public ScriptScenario(global::ClassicUO.CSharp.CSharpGameApi api, Func<int, Task> wait, string[] args, CancellationToken cancellation, Action checkpoint)
    {
        UO = api;
        WaitAsync = wait;
        Args = args;
        Cancellation = cancellation;
        Checkpoint = checkpoint;
    }

     public object? RunScenario()
    {
        using (var output = System.IO.File.Create("http-download.bin"))
        {
            UO.HTTP_Get("https://example.com/route-data.bin", output);
        }

        return UO.HTTP_Header();
        return null;
    }
}

๋ช…๋ น ๊ตฌํ˜„

C#์€ ๋Œ€์†Œ๋ฌธ์ž๋ฅผ ๊ตฌ๋ถ„ํ•ฉ๋‹ˆ๋‹ค. ์ •ํ™•ํ•œ UO ์ด๋ฆ„์„ ์‚ฌ์šฉํ•˜์„ธ์š”. ์ˆซ์ž ํ”Œ๋ž˜๊ทธ๋Š” 0๊ณผ ๋น„๊ตํ•˜๊ณ  ๋ฐ˜ํ™˜ ํ˜•์‹์ด bool์ผ ๋•Œ๋งŒ bool์„ ์‚ฌ์šฉํ•˜์„ธ์š”.

UO.HTTP_Get(url) โ€” UO.HTTP_Get(Any)

๋งค๊ฐœ๋ณ€์ˆ˜: url (Any)

๊ฒฐ๊ณผ: void / Unit.

ํ˜ธ์ถœ ์‹œ:

๋‚ด๋ถ€ ๊ตฌํ˜„ ์ฝ”๋“œ์ด๋ฉฐ ๋…๋ฆฝ ์Šคํฌ๋ฆฝํŠธ๋กœ ์‹คํ–‰ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

case "http_get":

                {

                    bool hasStream = arguments.Length > 1

                        && !(arguments[1].Kind == InjectionValueKind.Integer && arguments[1].Integer == 0)

                        && arguments[1].Kind != InjectionValueKind.Unit;

                    // Validate before network side effects and before changing the last response.

                    if (hasStream && !(arguments[1].Object is MemoryStreamObject))

                        throw new NativeSubrutineException("UO.HTTP_Get: LStream must be a writable System.IO.Stream, 0, or omitted.");



                    string[] response = bridge.HttpRequest("GET", Text(0), string.Empty);

                    httpHeader = response.Length > 1 ? response[1] : string.Empty;

                    httpBody = hasStream ? string.Empty : response.Length > 0 ? response[0] : string.Empty;

                    if (!hasStream)

                    {

                        return InjectionValue.Unit;

                    }



                    byte[] raw = response.Length > 3 && !string.IsNullOrEmpty(response[3])

                        ? Convert.FromBase64String(response[3])

                        : Array.Empty<byte>();

                    if (arguments[1].Object is MemoryStreamObject memoryStream)

                        memoryStream.ReplaceBytes(raw);

                    else

                        throw new NativeSubrutineException("UO.HTTP_Get: LStream must be a writable System.IO.Stream.");



                    // Stream overloads keep the response as bytes, not cached text.

                    return InjectionValue.Unit;

                }

UO.HTTP_Get(url, lStream) โ€” UO.HTTP_Get(Any,Any)

๋งค๊ฐœ๋ณ€์ˆ˜: url (Any), lStream (Any)

๊ฒฐ๊ณผ: void / Unit.

ํ˜ธ์ถœ ์‹œ:

๋‚ด๋ถ€ ๊ตฌํ˜„ ์ฝ”๋“œ์ด๋ฉฐ ๋…๋ฆฝ ์Šคํฌ๋ฆฝํŠธ๋กœ ์‹คํ–‰ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

if (name == "HTTP_Get" && arguments.Length == 2 && arguments[1] is Stream stream)
            {
                if (!stream.CanWrite) throw new ArgumentException("HTTP_Get requires a writable .NET stream.");
                // Adapt the existing binary response path without fetching the
                // response twice or converting its bytes through text encoding.
                var type = typeof(InjectionValue).Assembly.GetType("InjectionScript.Runtime.ObjectTypes.MemoryStreamObject", throwOnError: true)!;
                using var native = (IDisposable)Activator.CreateInstance(type, nonPublic: true)!;
                value = Invoke(name, [arguments[0], native]);
                var bytes = (InjectionValue)type.GetMethod("ToArray")!.Invoke(native, null)!;
                byte[] payload = bytes.Array.Select(item => checked((byte)item.Integer)).ToArray();
                if (stream.CanSeek) { stream.SetLength(0); stream.Position = 0; }
                stream.Write(payload);
                if (stream.CanSeek) stream.Position = 0;
            }
            else value = Invoke(name, arguments);

๋‚ด๋ถ€ ๊ตฌํ˜„ ์ฝ”๋“œ์ด๋ฉฐ ๋…๋ฆฝ ์Šคํฌ๋ฆฝํŠธ๋กœ ์‹คํ–‰ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

case "http_get":

                {

                    bool hasStream = arguments.Length > 1

                        && !(arguments[1].Kind == InjectionValueKind.Integer && arguments[1].Integer == 0)

                        && arguments[1].Kind != InjectionValueKind.Unit;

                    // Validate before network side effects and before changing the last response.

                    if (hasStream && !(arguments[1].Object is MemoryStreamObject))

                        throw new NativeSubrutineException("UO.HTTP_Get: LStream must be a writable System.IO.Stream, 0, or omitted.");



                    string[] response = bridge.HttpRequest("GET", Text(0), string.Empty);

                    httpHeader = response.Length > 1 ? response[1] : string.Empty;

                    httpBody = hasStream ? string.Empty : response.Length > 0 ? response[0] : string.Empty;

                    if (!hasStream)

                    {

                        return InjectionValue.Unit;

                    }



                    byte[] raw = response.Length > 3 && !string.IsNullOrEmpty(response[3])

                        ? Convert.FromBase64String(response[3])

                        : Array.Empty<byte>();

                    if (arguments[1].Object is MemoryStreamObject memoryStream)

                        memoryStream.ReplaceBytes(raw);

                    else

                        throw new NativeSubrutineException("UO.HTTP_Get: LStream must be a writable System.IO.Stream.");



                    // Stream overloads keep the response as bytes, not cached text.

                    return InjectionValue.Unit;

                }

UO.HTTP_Header

ะฃะบั€ะฐั—ะฝััŒะบะฐ ยท ะ ัƒััะบะธะน ยท English ยท Deutsch ยท Franรงais ยท Italiano ยท Espaรฑol ยท ็น้ซ”ไธญๆ–‡ ยท ๆ—ฅๆœฌ่ชž ยท ํ•œ๊ตญ์–ด

C# ์‹œ๊ทธ๋‹ˆ์ฒ˜

dynamic UO.HTTP_Header();

์ด ์Šคํฌ๋ฆฝํŠธ์˜ ๋งˆ์ง€๋ง‰ HTTP ์‘๋‹ต ํ—ค๋”(์˜ˆ: Content-Type)๋ฅผ ์ฝ์–ด ์„ ์–ธ๋œ ๋ฐ์ดํ„ฐ ํ˜•์‹์„ ์ฐพ์Šต๋‹ˆ๋‹ค. ์ด๋Š” ์‘๋‹ต ๋ณธ๋ฌธ, ์š”์ฒญ ํ—ค๋” ๋˜๋Š” ์ˆซ์ž ์ฝ”๋“œ HTTP์ด ์•„๋‹™๋‹ˆ๋‹ค.

๋งค๊ฐœ๋ณ€์ˆ˜

๋งค๊ฐœ๋ณ€์ˆ˜๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.

๋ฐ˜ํ™˜๊ฐ’

string โ€” ์ด๋ฆ„: ๊ฐ’ ํ˜•์‹์˜ ์ค„์ด๋ฉฐ CRLF๋กœ ๊ตฌ๋ถ„๋ฉ๋‹ˆ๋‹ค. ์ฒซ ๋ฒˆ์งธ ์š”์ฒญ ์ „๊ณผ ๋„คํŠธ์›Œํฌ ์˜ค๋ฅ˜ ํ›„ - ๋นˆ ์ค„์ž…๋‹ˆ๋‹ค. HTTP/1.1 200 OK ์ค„์€ ์—ฌ๊ธฐ์— ์—†์Šต๋‹ˆ๋‹ค. ์ƒํƒœ ์ฝ”๋“œ๋Š” ๋ณ„๋„์˜ ๋ช…๋ น์œผ๋กœ ํ‘œ์‹œ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ด๋ฆ„์˜ ์ˆœ์„œ์™€ ๋Œ€์†Œ๋ฌธ์ž๋Š” ๋ณด์žฅ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๋™์ž‘

  • ๋งˆ์ง€๋ง‰ ์‘๋‹ต์€ ์Šคํฌ๋ฆฝํŠธ์˜ ํ˜„์žฌ ๋Ÿฐํƒ€์ž„์—๋งŒ ์ €์žฅ๋ฉ๋‹ˆ๋‹ค. ์ด ๋Ÿฐํƒ€์ž„์˜ ๋‹ค์Œ GET/POST๊ฐ€ ์ด๋ฅผ ๋Œ€์ฒดํ•ฉ๋‹ˆ๋‹ค. ๋™์‹œ์— ์‹คํ–‰๋˜๋Š” ๋‹ค๋ฅธ ์Šคํฌ๋ฆฝํŠธ๋Š” ์ด๋ฅผ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋‹ต๋ณ€์€ ํ”„๋กœํ•„์— ์ €์žฅ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ ์š”์ฒญ ์ดํ›„์— ํ•„์š”ํ•˜๋‹ค๋ฉด ๋ณธ๋ฌธ๊ณผ ํ—ค๋”๋ฅผ ๋ฏธ๋ฆฌ ๋ณ€์ˆ˜์— ๋ณต์‚ฌํ•ด ๋‘์„ธ์š”.
  • GET/POST ์‹คํ–‰ ์ค‘์ธ ์Šคํฌ๋ฆฝํŠธ ์Šค๋ ˆ๋“œ์—์„œ ์‘๋‹ต์„ ๊ธฐ๋‹ค๋ฆฝ๋‹ˆ๋‹ค. ๋ณธ๋ฌธ์„ ์š”์ฒญํ•˜๊ณ  ์ฝ๋Š” ๋ฐ ๊ฑธ๋ฆฌ๋Š” ์ด ์‹œ๊ฐ„์€ 20์ดˆ์ž…๋‹ˆ๋‹ค. ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์ค‘์ง€ํ•˜๋ฉด ๋Œ€๊ธฐ๊ฐ€ ์ทจ์†Œ๋ฉ๋‹ˆ๋‹ค. HTTP_Body/HTTP_Header ์•„๋ฌด๊ฒƒ๋„ ๊ธฐ๋Œ€ํ•˜์ง€ ๋งˆ์„ธ์š”. FileStream๊ฐ€ ์ „์†ก๋˜๋Š” ๊ฒฝ์šฐ์—๋„ ์ „์ฒด ๋ณธ๋ฌธ์ด ๋ฉ”๋ชจ๋ฆฌ์— ๋กœ๋“œ๋ฉ๋‹ˆ๋‹ค. ์ด๋Š” ๋Œ€์šฉ๋Ÿ‰ ํŒŒ์ผ์˜ ์ŠคํŠธ๋ฆฌ๋ฐ ๋‹ค์šด๋กœ๋“œ๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค.
  • ์ธ์ฝ”๋”ฉ์ด ์—†๊ฑฐ๋‚˜ ์•Œ ์ˆ˜ ์—†๋Š” ๊ฒฝ์šฐ(UTF-8) ํ…์ŠคํŠธ๋Š” ์‘๋‹ต์˜ ๋ฌธ์ž ์„ธํŠธ์— ๋”ฐ๋ผ ๋””์ฝ”๋”ฉ๋ฉ๋‹ˆ๋‹ค. ๋‹ต๋ณ€ HTTP 4xx/5xx๋„ ์ฝ์Šต๋‹ˆ๋‹ค. ๋„คํŠธ์›Œํฌ ์˜ค๋ฅ˜๋‚˜ ์‹œ๊ฐ„ ์ดˆ๊ณผ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ํด๋ผ์ด์–ธํŠธ๋Š” ์˜ค๋ฅ˜๋ฅผ ๊ธฐ๋กํ•˜๊ณ  ๋ณธ๋ฌธ๊ณผ ํ—ค๋”๋Š” ๋น„์–ด ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. Try/Catch๋Š” ์˜ˆ์ƒ ์‘๋‹ต ํ™•์ธ์„ ๋Œ€์ฒดํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์Šคํฌ๋ฆฝํŠธ ์ทจ์†Œ๊ฐ€ ๋”์šฑ ํ™•์žฅ๋ฉ๋‹ˆ๋‹ค. ๋กœ์ปฌ ํŒŒ์ผ ์“ฐ๊ธฐ ๋˜๋Š” ์ž˜๋ชป๋œ ์ธ์ˆ˜ ์˜ค๋ฅ˜๋Š” Try/Catch๋ฅผ ํ†ตํ•ด ํฌ์ฐฉ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • HTTP_Header๋Š” ์‘๋‹ต ํ—ค๋”์™€ ๋‚ด์šฉ์„ ๊ฒฐํ•ฉํ•ฉ๋‹ˆ๋‹ค. ๋ฐ”์ด๋„ˆ๋ฆฌ GET ์ดํ›„์—๋„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฆ„์€ ๋Œ€์†Œ๋ฌธ์ž๋ฅผ ๊ตฌ๋ถ„ํ•˜์ง€ ์•Š๊ณ  ํ™•์ธํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. Content-Type์€ ์ฝ˜ํ…์ธ  ๊ฒ€์‚ฌ๊ฐ€ ์•„๋‹Œ ์„œ๋ฒ„ ๋ฌธ์ž…๋‹ˆ๋‹ค. ๋‹ค์šด๋กœ๋“œํ•œ ํ…์ŠคํŠธ๋ฅผ ์ฝ”๋“œ๋กœ ์‹คํ–‰ํ•˜์ง€ ๋งˆ์„ธ์š”.

๋…๋ฆฝ ์‹คํ–‰ C# ์Šคํฌ๋ฆฝํŠธ

๊ฐ ์˜ˆ์ œ๋Š” ๋ณ„๋„์˜ ํŒŒ์ผ .csx๋กœ ์ €์žฅํ•˜๊ณ  IDE์—์„œ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. UO ๊ฐœ์ฒด์™€ ๋Œ€๊ธฐ ๊ธฐ๋Šฅ์€ ํ˜„์žฌ ๊ฒŒ์ž„ ์„ธ์…˜์˜ ํ˜ธ์ŠคํŠธ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ๋ฐ๋ชจ serial ์ด๋ฆ„, ์ขŒํ‘œ, ์ฃผ์†Œ๋ฅผ ์ž์‹ ์˜ ๊ฒƒ์œผ๋กœ ๋ฐ”๊พธ์„ธ์š”.

๊ฐ„๋‹จํ•œ ์˜ˆ์ œ

์ด ์ฝ”๋“œ๋ฅผ ๋ณ„๋„์˜ .csx ํŒŒ์ผ๋กœ ์‹คํ–‰ํ•˜์„ธ์š”. ์ธ์ˆ˜๋Š” ํ˜ธ์ถœ์— ์ง์ ‘ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค. ์˜ˆ์ œ์˜ ์ด๋ฆ„, serial, ์ฃผ์†Œ๋ฅผ ์ž์‹ ์˜ ๊ฐ’์œผ๋กœ ๋ฐ”๊พธ์„ธ์š”.

ํ•œ ๋ฒˆ์˜ ํ˜ธ์ถœ ๊ฒฐ๊ณผ๋ฅผ ์ €๋„์— ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค. ์ˆซ์ž๋ฅผ bool๋กœ ๋ณ€ํ™˜ํ•˜์ง€ ์•Š์œผ๋ฉฐ, ๋ฐฐ์—ด์ด๋ฉด ์š”์†Œ ์ˆ˜๋ฅผ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค.

UO.HTTP_Get("https://example.com/");
object? result = UO.HTTP_Header();
UO.Print(result is Array values ? "Count: " + values.Length : Convert.ToString(result));

1. ๋Œ€์†Œ๋ฌธ์ž ๊ตฌ๋ถ„ ์—†์ด ์‘๋‹ต ํ—ค๋” ์ฝ๊ธฐ - ์ง์ ‘ ์Šคํฌ๋ฆฝํŠธ

๋งค๊ฐœ๋ณ€์ˆ˜์™€ ์‹คํ–‰ ํ๋ฆ„:

  • ์ œ๋ชฉ์€ ํ•œ ์ค„์”ฉ ๊ตฌ๋ฌธ ๋ถ„์„๋ฉ๋‹ˆ๋‹ค. Content-Type ์ด๋ฆ„์€ ๋Œ€์†Œ๋ฌธ์ž๋ฅผ ๊ตฌ๋ถ„ํ•˜์ง€ ์•Š๊ณ  ๋น„๊ต๋ฉ๋‹ˆ๋‹ค. ๋ฌธ์ž์—ด์ด ์—†์œผ๋ฉด ๋นˆ ๋ฌธ์ž์—ด์ด ๋ฐ˜ํ™˜๋ฉ๋‹ˆ๋‹ค.
UO.HTTP_Get("https://example.com/csharp-status");
return ReadHeader(Convert.ToString(UO.HTTP_Header()) ?? "", "Content-Type");

string ReadHeader(string headers, string name)
{
    foreach (string line in headers.Split(new[] { "\r\n", "\n" }, StringSplitOptions.RemoveEmptyEntries))
    {
        int separator = line.IndexOf(':');
        if (separator > 0 && line[..separator].Trim().Equals(name, StringComparison.OrdinalIgnoreCase))
            return line[(separator + 1)..].Trim();
    }
    return "";
}

2. ์„ ์–ธ๋œ JSON Content-Type ํ™•์ธ - ์Šคํฌ๋ฆฝํŠธ ๊ธฐ๋Šฅ

๋งค๊ฐœ๋ณ€์ˆ˜์™€ ์‹คํ–‰ ํ๋ฆ„:

  • ๋กœ์ปฌ์— ์ €์žฅ๋œ ํ—ค๋” ํ…์ŠคํŠธ๊ฐ€ ํ™•์ธ๋ฉ๋‹ˆ๋‹ค. Content-Type ์ž์ฒด๋Š” JSON์˜ ์ •ํ™•์„ฑ์„ ๋ณด์žฅํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. System.Text.Json์˜ ๋‚ด์šฉ์„ ๋ณ„๋„๋กœ ํ™•์ธํ•˜์„ธ์š”.
return  RunScenario();
 object? RunScenario()
{
    UO.HTTP_Get("https://example.com/csharp-status");
    string headers = Convert.ToString(UO.HTTP_Header()) ?? "";
    return headers.Contains("application/json", StringComparison.OrdinalIgnoreCase);
    return null;
}

3. ๋ฐ”์ด๋„ˆ๋ฆฌ ์‘๋‹ต ํ—ค๋” ์ €์žฅ - ์Šคํฌ๋ฆฝํŠธ ํด๋ž˜์Šค

๋งค๊ฐœ๋ณ€์ˆ˜์™€ ์‹คํ–‰ ํ๋ฆ„:

  • GET๋Š” ์†Œ์Šค ๋ฐ”์ดํŠธ๋ฅผ System.IO.MemoryStream๋กœ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค. ๋งˆ์ง€๋ง‰ ์‘๋‹ต ํ—ค๋”๋Š” ์—ฌ์ „ํžˆ HTTP_Header;์ž…๋‹ˆ๋‹ค. ์ด ์–‘์‹์˜ ํ…์ŠคํŠธ HTTP_Body๊ฐ€ ๋น„์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ํ—ค๋”๋งŒ ํŒŒ์ผ์— ์ €์žฅ๋ฉ๋‹ˆ๋‹ค.
return  new ScriptScenario(UO, WaitAsync, Args, Cancellation, Checkpoint).RunScenario();
class ScriptScenario
{
    private readonly global::ClassicUO.CSharp.CSharpGameApi UO;
    private readonly Func<int, Task> WaitAsync;
    private readonly string[] Args;
    private readonly CancellationToken Cancellation;
    private readonly Action Checkpoint;
    public ScriptScenario(global::ClassicUO.CSharp.CSharpGameApi api, Func<int, Task> wait, string[] args, CancellationToken cancellation, Action checkpoint)
    {
        UO = api;
        WaitAsync = wait;
        Args = args;
        Cancellation = cancellation;
        Checkpoint = checkpoint;
    }

     public object? RunScenario()
    {
        using (var data = new System.IO.MemoryStream())
        {
            UO.HTTP_Get("https://example.com/route-data.bin", data);
            string headers = Convert.ToString(UO.HTTP_Header()) ?? "";
            System.IO.File.WriteAllText("http-response-headers.txt", headers);
            UO.Print($"Saved response headers; downloaded {data.Length} binary bytes.");
            return headers;
        }

        return null;
    }
}

๋ช…๋ น ๊ตฌํ˜„

C#์€ ๋Œ€์†Œ๋ฌธ์ž๋ฅผ ๊ตฌ๋ถ„ํ•ฉ๋‹ˆ๋‹ค. ์ •ํ™•ํ•œ UO ์ด๋ฆ„์„ ์‚ฌ์šฉํ•˜์„ธ์š”. ์ˆซ์ž ํ”Œ๋ž˜๊ทธ๋Š” 0๊ณผ ๋น„๊ตํ•˜๊ณ  ๋ฐ˜ํ™˜ ํ˜•์‹์ด bool์ผ ๋•Œ๋งŒ bool์„ ์‚ฌ์šฉํ•˜์„ธ์š”.

UO.HTTP_Header() โ€” UO.HTTP_Header()

๋งค๊ฐœ๋ณ€์ˆ˜: ๋งค๊ฐœ๋ณ€์ˆ˜๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.

๊ฒฐ๊ณผ: dynamic / Any.

ํ˜ธ์ถœ ์‹œ:

๋‚ด๋ถ€ ๊ตฌํ˜„ ์ฝ”๋“œ์ด๋ฉฐ ๋…๋ฆฝ ์Šคํฌ๋ฆฝํŠธ๋กœ ์‹คํ–‰ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

case "http_header": return new InjectionValue(httpHeader);

UO.HTTP_Post

ะฃะบั€ะฐั—ะฝััŒะบะฐ ยท ะ ัƒััะบะธะน ยท English ยท Deutsch ยท Franรงais ยท Italiano ยท Espaรฑol ยท ็น้ซ”ไธญๆ–‡ ยท ๆ—ฅๆœฌ่ชž ยท ํ•œ๊ตญ์–ด

C# ์‹œ๊ทธ๋‹ˆ์ฒ˜

dynamic UO.HTTP_Post(object? url, object? postData);

POST ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ HTTP ์„œ๋น„์Šค์— ํ…์ŠคํŠธ๋ฅผ ๋ณด๋‚ด๊ณ  ์‘๋‹ต ํ…์ŠคํŠธ๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ์Šคํฌ๋ฆฝํŠธ๋Š” ๊ฒฝ๋กœ ์ค‘์ง€๋ฅผ ๋ณด๊ณ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Š” ์ด๋ฏธ ๋งŒ๋“ค์–ด์ง„ Telegram/Discord ๋ช…๋ น์ด ์•„๋‹™๋‹ˆ๋‹ค. ํ•ด๋‹น ํ”„๋กœํ† ์ฝœ, ์ธ์ฆ ๋ฐ ์š”์ฒญ ํ˜•์‹์€ ๋ณ„๋„์˜ ์ง€์›์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

๋งค๊ฐœ๋ณ€์ˆ˜

  • url - ํ•„์ˆ˜ string: ์ „์ฒด ์ฃผ์†Œ http:// ๋˜๋Š” https://. example.com์„ ์„œ๋น„์Šค ์ฃผ์†Œ๋กœ ๋ฐ”๊ฟ‰๋‹ˆ๋‹ค. ์ด ์š”์ฒญ์—๋Š” ๊ฒŒ์ž„ ๋ฐ ๊ฒŒ์ž„ ์„œ๋ฒ„๊ฐ€ ํ•„์š”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ฒŒ์ž„ ํ”„๋ก์‹œ ํ”„๋กœํ•„์ด ์•„๋‹Œ ํ”„๋กœ์„ธ์Šค์˜ ์ผ๋ฐ˜ ๋„คํŠธ์›Œํฌ ์—ฐ๊ฒฐ์ด ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.
  • postData โ€” ์š”์ฒญ ๋ณธ๋ฌธ์ด ์žˆ๋Š” ํ•„์ˆ˜ string์ž…๋‹ˆ๋‹ค. UTF-8์—์„œ ์ฝ˜ํ…์ธ  ์œ ํ˜•: text/plain; charset=utf-8. ์ž๋™ ์–‘์‹ ์ธ์ฝ”๋”ฉ ๋ฐ application/json์œผ๋กœ์˜ ์ „ํ™˜์€ ์—†์Šต๋‹ˆ๋‹ค. System.Text.Json.JsonSerializer.Serialize๋Š” ํ…์ŠคํŠธ๋งŒ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ์ œ ์„œ๋ฒ„๋Š” JSON๋ฅผ ์ •ํ™•ํ•˜๊ฒŒ text/plain์œผ๋กœ ํ—ˆ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ๋ช…๋ น์„ ์‚ฌ์šฉํ•˜์—ฌ ์‚ฌ์šฉ์ž ์ •์˜ ํ—ค๋”๋ฅผ ๊ตฌ์„ฑํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

๋ฐ˜ํ™˜๊ฐ’

string โ€” ์‘๋‹ต ํ…์ŠคํŠธ; HTTP_Body()๋ฅผ ํ†ตํ•ด์„œ๋„ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Š” bool๋„ ์•„๋‹ˆ๊ณ  ์ฝ”๋“œ HTTP๋„ ์•„๋‹™๋‹ˆ๋‹ค. ๋นˆ ๋ฌธ์ž์—ด์€ ์˜ค๋ฅ˜๋ฅผ ๋‚˜ํƒ€๋‚ด์ง€ ์•Š์ง€๋งŒ, ๋น„์–ด ์žˆ์ง€ ์•Š์€ ๋ฌธ์ž์—ด์€ ์„œ๋ฒ„์—์„œ์˜ ์ž‘์—… ์„ฑ๊ณต์„ ๋‚˜ํƒ€๋‚ด์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๋™์ž‘

  • ๋งˆ์ง€๋ง‰ ์‘๋‹ต์€ ์Šคํฌ๋ฆฝํŠธ์˜ ํ˜„์žฌ ๋Ÿฐํƒ€์ž„์—๋งŒ ์ €์žฅ๋ฉ๋‹ˆ๋‹ค. ์ด ๋Ÿฐํƒ€์ž„์˜ ๋‹ค์Œ GET/POST๊ฐ€ ์ด๋ฅผ ๋Œ€์ฒดํ•ฉ๋‹ˆ๋‹ค. ๋™์‹œ์— ์‹คํ–‰๋˜๋Š” ๋‹ค๋ฅธ ์Šคํฌ๋ฆฝํŠธ๋Š” ์ด๋ฅผ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋‹ต๋ณ€์€ ํ”„๋กœํ•„์— ์ €์žฅ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ ์š”์ฒญ ์ดํ›„์— ํ•„์š”ํ•˜๋‹ค๋ฉด ๋ณธ๋ฌธ๊ณผ ํ—ค๋”๋ฅผ ๋ฏธ๋ฆฌ ๋ณ€์ˆ˜์— ๋ณต์‚ฌํ•ด ๋‘์„ธ์š”.
  • GET/POST ์‹คํ–‰ ์ค‘์ธ ์Šคํฌ๋ฆฝํŠธ ์Šค๋ ˆ๋“œ์—์„œ ์‘๋‹ต์„ ๊ธฐ๋‹ค๋ฆฝ๋‹ˆ๋‹ค. ๋ณธ๋ฌธ์„ ์š”์ฒญํ•˜๊ณ  ์ฝ๋Š” ๋ฐ ๊ฑธ๋ฆฌ๋Š” ์ด ์‹œ๊ฐ„์€ 20์ดˆ์ž…๋‹ˆ๋‹ค. ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์ค‘์ง€ํ•˜๋ฉด ๋Œ€๊ธฐ๊ฐ€ ์ทจ์†Œ๋ฉ๋‹ˆ๋‹ค. HTTP_Body/HTTP_Header ์•„๋ฌด๊ฒƒ๋„ ๊ธฐ๋Œ€ํ•˜์ง€ ๋งˆ์„ธ์š”. FileStream๊ฐ€ ์ „์†ก๋˜๋Š” ๊ฒฝ์šฐ์—๋„ ์ „์ฒด ๋ณธ๋ฌธ์ด ๋ฉ”๋ชจ๋ฆฌ์— ๋กœ๋“œ๋ฉ๋‹ˆ๋‹ค. ์ด๋Š” ๋Œ€์šฉ๋Ÿ‰ ํŒŒ์ผ์˜ ์ŠคํŠธ๋ฆฌ๋ฐ ๋‹ค์šด๋กœ๋“œ๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค.
  • ์ธ์ฝ”๋”ฉ์ด ์—†๊ฑฐ๋‚˜ ์•Œ ์ˆ˜ ์—†๋Š” ๊ฒฝ์šฐ(UTF-8) ํ…์ŠคํŠธ๋Š” ์‘๋‹ต์˜ ๋ฌธ์ž ์„ธํŠธ์— ๋”ฐ๋ผ ๋””์ฝ”๋”ฉ๋ฉ๋‹ˆ๋‹ค. ๋‹ต๋ณ€ HTTP 4xx/5xx๋„ ์ฝ์Šต๋‹ˆ๋‹ค. ๋„คํŠธ์›Œํฌ ์˜ค๋ฅ˜๋‚˜ ์‹œ๊ฐ„ ์ดˆ๊ณผ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ํด๋ผ์ด์–ธํŠธ๋Š” ์˜ค๋ฅ˜๋ฅผ ๊ธฐ๋กํ•˜๊ณ  ๋ณธ๋ฌธ๊ณผ ํ—ค๋”๋Š” ๋น„์–ด ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. Try/Catch๋Š” ์˜ˆ์ƒ ์‘๋‹ต ํ™•์ธ์„ ๋Œ€์ฒดํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์Šคํฌ๋ฆฝํŠธ ์ทจ์†Œ๊ฐ€ ๋”์šฑ ํ™•์žฅ๋ฉ๋‹ˆ๋‹ค. ๋กœ์ปฌ ํŒŒ์ผ ์“ฐ๊ธฐ ๋˜๋Š” ์ž˜๋ชป๋œ ์ธ์ˆ˜ ์˜ค๋ฅ˜๋Š” Try/Catch๋ฅผ ํ†ตํ•ด ํฌ์ฐฉ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • POST ๋ณธ๋ฌธ๊ณผ ํ—ค๋”๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๊ณ  ๋™์ผํ•œ ๋ณธ๋ฌธ์„ ๋ฌธ์ž์—ด๋กœ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ์‘๋‹ต์ด ๋น„์–ด ์žˆ๋‹ค๋Š” ์ด์œ ๋งŒ์œผ๋กœ POST์„ ์ž๋™์œผ๋กœ ๋ฐ˜๋ณตํ•˜์ง€ ๋งˆ์„ธ์š”. ์„œ๋ฒ„๊ฐ€ ์ด๋ฏธ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ–ˆ์„ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ์ œ์˜ ์Šน์ธ/์ƒํƒœ ํ™•์ธ์€ HTTP-์„ฑ๊ณต์— ๋Œ€ํ•œ ๊ธฐ๋ณธ ์ œ๊ณต ๊ธฐํ˜ธ๊ฐ€ ์•„๋‹ˆ๋ผ ์„œ๋น„์Šค์— ๋Œ€ํ•œ ๋™์˜์ž…๋‹ˆ๋‹ค.

๋…๋ฆฝ ์‹คํ–‰ C# ์Šคํฌ๋ฆฝํŠธ

๊ฐ ์˜ˆ์ œ๋Š” ๋ณ„๋„์˜ ํŒŒ์ผ .csx๋กœ ์ €์žฅํ•˜๊ณ  IDE์—์„œ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. UO ๊ฐœ์ฒด์™€ ๋Œ€๊ธฐ ๊ธฐ๋Šฅ์€ ํ˜„์žฌ ๊ฒŒ์ž„ ์„ธ์…˜์˜ ํ˜ธ์ŠคํŠธ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ๋ฐ๋ชจ serial ์ด๋ฆ„, ์ขŒํ‘œ, ์ฃผ์†Œ๋ฅผ ์ž์‹ ์˜ ๊ฒƒ์œผ๋กœ ๋ฐ”๊พธ์„ธ์š”.

๊ฐ„๋‹จํ•œ ์˜ˆ์ œ

์ด ์ฝ”๋“œ๋ฅผ ๋ณ„๋„์˜ .csx ํŒŒ์ผ๋กœ ์‹คํ–‰ํ•˜์„ธ์š”. ์ธ์ˆ˜๋Š” ํ˜ธ์ถœ์— ์ง์ ‘ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค. ์˜ˆ์ œ์˜ ์ด๋ฆ„, serial, ์ฃผ์†Œ๋ฅผ ์ž์‹ ์˜ ๊ฐ’์œผ๋กœ ๋ฐ”๊พธ์„ธ์š”.

ํ•œ ๋ฒˆ์˜ ํ˜ธ์ถœ ๊ฒฐ๊ณผ๋ฅผ ์ €๋„์— ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค. ์ˆซ์ž๋ฅผ bool๋กœ ๋ณ€ํ™˜ํ•˜์ง€ ์•Š์œผ๋ฉฐ, ๋ฐฐ์—ด์ด๋ฉด ์š”์†Œ ์ˆ˜๋ฅผ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค.

object? result = UO.HTTP_Post("https://example.com/", "Hello from C#");
UO.Print(result is Array values ? "Count: " + values.Length : Convert.ToString(result));

1. ํ•ฉ์˜๋œ ์„œ๋น„์Šค ์‘๋‹ต ํ™•์ธ - ์ง์ ‘ ์Šคํฌ๋ฆฝํŠธ

๋งค๊ฐœ๋ณ€์ˆ˜์™€ ์‹คํ–‰ ํ๋ฆ„:

  • ์„œ๋น„์Šค๋Š” Route ์ค‘์ง€๋จ ํ…์ŠคํŠธ๋ฅผ ์ˆ˜๋ฝํ•˜๊ณ  ๋™์˜์— ๋”ฐ๋ผ ์ˆ˜๋ฝ๋จ์œผ๋กœ ์‘๋‹ตํ•ฉ๋‹ˆ๋‹ค. Main์€ ์ด ํ…์ŠคํŠธ๋กœ๋งŒ true๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” HTTP ์ฝ”๋“œ ํ™•์ธ์ด ์•„๋‹Œ ์„œ๋น„์Šค ํ”„๋กœํ† ์ฝœ ํ™•์ธ์ž…๋‹ˆ๋‹ค.
  • example.com ์ฃผ์†Œ๋Š” ์˜ˆ์‹œ์ผ ๋ฟ์ด๋ฉฐ ๊ทธ๋Ÿฌํ•œ ์„œ๋น„์Šค๋ฅผ ์ œ๊ณตํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ž์‹ ์˜ ๊ฒƒ์œผ๋กœ ๊ต์ฒดํ•˜์‹ญ์‹œ์˜ค. ์˜ˆ์ œ๋ฅผ ์‹คํ–‰ํ•˜๋ฉด ์‹ค์ œ HTTP ์ฟผ๋ฆฌ๊ฐ€ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค. ์ด ์˜ˆ์—์„œ๋Š” ๊ฒŒ์ž„ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
var reply = UO.HTTP_Post("https://example.com/C#-event", "Route stopped");
return Truth(TrimText(reply) == "accepted");

bool Truth(object? value) => value is bool flag ? flag : value is string text ? !string.IsNullOrEmpty(text) : value != null && Convert.ToDouble(value) != 0;

string TrimText(object? value) => (Convert.ToString(value) ?? "").Trim();

2. ์ง๋ ฌํ™”๋œ JSON ์ „์†ก ๋ฐ JSON ์‘๋‹ต ๊ตฌ๋ฌธ ๋ถ„์„ - ์Šคํฌ๋ฆฝํŠธ ๊ธฐ๋Šฅ

๋งค๊ฐœ๋ณ€์ˆ˜์™€ ์‹คํ–‰ ํ๋ฆ„:

  • System.Text.Json.JsonSerializer๋Š” ์‚ฌ์ „ ๋ฌธ์ž์—ด์„ ์ด์Šค์ผ€์ดํ”„ํ•ฉ๋‹ˆ๋‹ค. JSON๋ฅผ ํ™•์ธํ•œ ํ›„ ์ƒํƒœ ๊ฐ’์„ ์ฝ์Šต๋‹ˆ๋‹ค. ๋ฐ๋ชจ ์ฃผ์†Œ๋ฅผ ๊ท€ํ•˜์˜ ๊ฒƒ์œผ๋กœ ๋ฐ”๊พธ์‹ญ์‹œ์˜ค.
return  RunScenario();
 object? RunScenario()
{
    var report = new Dictionary<string, string>
    {
        ["event"] = "route-stopped",
        ["text"] = "line one\nline two"
    };
    string payload = System.Text.Json.JsonSerializer.Serialize(report);
    string reply = Convert.ToString(UO.HTTP_Post("https://example.com/csharp-report", payload)) ?? "";
    try
    {
        using (var document = System.Text.Json.JsonDocument.Parse(reply))
        {
            return document.RootElement.TryGetProperty("status", out var value) ? value.GetString() : "unknown";
        }
    }
    catch (System.Text.Json.JsonException)
    {
        return "invalid response";
    }

    return null;
}

3. ๋‹ค์Œ ์š”์ฒญ๊นŒ์ง€ ์‘๋‹ต ์ €์žฅ - ์Šคํฌ๋ฆฝํŠธ ํด๋ž˜์Šค

๋งค๊ฐœ๋ณ€์ˆ˜์™€ ์‹คํ–‰ ํ๋ฆ„:

  • firstReply ๋ฐ firstHeaders๋Š” ๋‹ต๋ณ€ POST๋ฅผ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค. ํ›„์† GET๋Š” ๋‚ด๋ถ€ ๋ฒ„ํผ๋ฅผ ๋Œ€์ฒดํ•˜์ง€๋งŒ ๋กœ์ปฌ ๋ณ€์ˆ˜์—๋Š” ์—ฌ์ „ํžˆ ์ฒซ ๋ฒˆ์งธ ์‘๋‹ต์ด ํฌํ•จ๋ฉ๋‹ˆ๋‹ค.
  • example.com ์ฃผ์†Œ๋Š” ์˜ˆ์‹œ์ผ ๋ฟ์ด๋ฉฐ ๊ทธ๋Ÿฌํ•œ ์„œ๋น„์Šค๋ฅผ ์ œ๊ณตํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ž์‹ ์˜ ๊ฒƒ์œผ๋กœ ๊ต์ฒดํ•˜์‹ญ์‹œ์˜ค. ์˜ˆ์ œ๋ฅผ ์‹คํ–‰ํ•˜๋ฉด ์‹ค์ œ HTTP ์ฟผ๋ฆฌ๊ฐ€ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค. ์ด ์˜ˆ์—์„œ๋Š” ๊ฒŒ์ž„ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
return  new ScriptScenario(UO, WaitAsync, Args, Cancellation, Checkpoint).RunScenario();
class ScriptScenario
{
    private readonly global::ClassicUO.CSharp.CSharpGameApi UO;
    private readonly Func<int, Task> WaitAsync;
    private readonly string[] Args;
    private readonly CancellationToken Cancellation;
    private readonly Action Checkpoint;
    public ScriptScenario(global::ClassicUO.CSharp.CSharpGameApi api, Func<int, Task> wait, string[] args, CancellationToken cancellation, Action checkpoint)
    {
        UO = api;
        WaitAsync = wait;
        Args = args;
        Cancellation = cancellation;
        Checkpoint = checkpoint;
    }

     public object? RunScenario()
    {
        var firstReply = UO.HTTP_Post("https://example.com/C#-event", "Route stopped");
        var firstHeaders = UO.HTTP_Header();
        UO.HTTP_Get("https://example.com/C#-status");
        return firstReply + "|" + firstHeaders;
        return null;
    }
}

๋ช…๋ น ๊ตฌํ˜„

C#์€ ๋Œ€์†Œ๋ฌธ์ž๋ฅผ ๊ตฌ๋ถ„ํ•ฉ๋‹ˆ๋‹ค. ์ •ํ™•ํ•œ UO ์ด๋ฆ„์„ ์‚ฌ์šฉํ•˜์„ธ์š”. ์ˆซ์ž ํ”Œ๋ž˜๊ทธ๋Š” 0๊ณผ ๋น„๊ตํ•˜๊ณ  ๋ฐ˜ํ™˜ ํ˜•์‹์ด bool์ผ ๋•Œ๋งŒ bool์„ ์‚ฌ์šฉํ•˜์„ธ์š”.

UO.HTTP_Post(url, postData) โ€” UO.HTTP_Post(Any,Any)

๋งค๊ฐœ๋ณ€์ˆ˜: url (Any), postData (Any)

๊ฒฐ๊ณผ: dynamic / Any.

ํ˜ธ์ถœ ์‹œ:

๋‚ด๋ถ€ ๊ตฌํ˜„ ์ฝ”๋“œ์ด๋ฉฐ ๋…๋ฆฝ ์Šคํฌ๋ฆฝํŠธ๋กœ ์‹คํ–‰ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

case "http_post":

                {

                    string[] response = bridge.HttpRequest("POST", Text(0), Text(1));

                    httpBody = response.Length > 0 ? response[0] : string.Empty;

                    httpHeader = response.Length > 1 ? response[1] : string.Empty;

                    return new InjectionValue(httpBody);

                }

UO.Messenger

ะฃะบั€ะฐั—ะฝััŒะบะฐ ยท ะ ัƒััะบะธะน ยท English ยท Deutsch ยท Franรงais ยท Italiano ยท Espaรฑol ยท ็น้ซ”ไธญๆ–‡ ยท ๆ—ฅๆœฌ่ชž ยท ํ•œ๊ตญ์–ด

C# ์‹œ๊ทธ๋‹ˆ์ฒ˜

dynamic UO.Messenger(object? provider);

๋ฌธ์ž ์•Œ๋ฆผ ๋ฐ Telegram, Discord ๋˜๋Š” Viber ๋ฉ”์‹œ์ง€ ์ˆ˜์‹ ์„ ์œ„ํ•œ ๊ฐœ์ฒด๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ์Šคํฌ๋ฆฝํŠธ๋Š” ๋ฆฌ์†Œ์Šค ์ˆ˜์ง‘ ์ข…๋ฃŒ๋ฅผ ์•Œ๋ฆฌ๊ฑฐ๋‚˜ ๋ฏธ๋ฆฌ ์ง€์ •๋œ ์‚ฌ์šฉ์ž์˜ ์ค‘์ง€ ์š”์ฒญ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ฐœ์ฒด๋ฅผ ์ƒ์„ฑํ•ด๋„ ์•„์ง ๋ด‡์— ์—ฐ๊ฒฐ๋˜์ง€ ์•Š์œผ๋ฉฐ ์•„๋ฌด ๊ฒƒ๋„ ๋ณด๋‚ด์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๋งค๊ฐœ๋ณ€์ˆ˜

  • provider - string: "์ „๋ณด", "๋””์Šค์ฝ”๋“œ" ๋˜๋Š” "๋ฐ”์ด๋ฒ„"; ๊ฒฝ์šฐ๋Š” ์ค‘์š”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋‹ค๋ฅธ ๊ฐ’์€ ์˜ค๋ฅ˜๋ฅผ ๋ฐœ์ƒ์‹œํ‚ต๋‹ˆ๋‹ค.

๋ฐ˜ํ™˜๊ฐ’

๊ฐœ์ฒด Messenger. ์ด๊ฒƒ์€ bool๋„ ์•„๋‹ˆ๊ณ  ID๋„ ์•„๋‹™๋‹ˆ๋‹ค. ID ์ด ๊ฐœ์ฒด์˜ ๋ฉ”์‹œ์ง€, ๋ณด๋‚ธ ์‚ฌ๋žŒ ๋ฐ ์ฑ„ํŒ…์€ ํ•ญ์ƒ string์ž…๋‹ˆ๋‹ค. ๋ฌธ์ž์—ด๊ณผ ๋น„๊ตํ•˜๊ณ  Convert.ToInt32/Convert.ToDouble๋ฅผ ํ†ตํ•ด ๋ณ€ํ™˜ํ•˜์ง€ ๋งˆ์„ธ์š”.

๋™์ž‘

  • Provider() โ†’ string; Connected() โ†’ 1/0 (true/false) : ๋ฒค๋” ๋ฐ ์ตœ์ข… ์Šน์ธ ํ™•์ธ ๊ฒฐ๊ณผ. Connected๋Š” ํ˜„์žฌ ์ธํ„ฐ๋„ท ๊ฐ€์šฉ์„ฑ์„ ๋ณด์žฅํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
  • Connect(token) โ†’ null: ๋ด‡ ํ† ํฐ์„ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค. ConnectFromFile(path) โ†’ null: ํŒŒ์ผ์—์„œ ์ตœ๋Œ€ 1024 ๋ฐ”์ดํŠธ๊นŒ์ง€ UTF-8 ํ† ํฐ์„ ์ฝ์Šต๋‹ˆ๋‹ค. ์ƒ๋Œ€ ๊ฒฝ๋กœ๋Š” ํ˜„์žฌ ์Šคํฌ๋ฆฝํŠธ ํด๋”์—์„œ ๊ณ„์‚ฐ๋ฉ๋‹ˆ๋‹ค. ๋‹ค์‹œ ์—ฐ๊ฒฐํ•˜๋ ค๋ฉด Disconnect๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. Try/Catch๋ฅผ ํ†ตํ•ด ์˜ค๋ฅ˜๋ฅผ ํฌ์ฐฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • Disconnect() โ†’ null: ๋กœ์ปฌ ์ˆ˜์‹ ์„ ์ค‘์ง€ํ•˜๊ณ  ํ† ํฐ๊ณผ ์ปค์„œ๋ฅผ ์ง€์›๋‹ˆ๋‹ค. ๊ฐœ์ฒด๋ฅผ ๋‹ค์‹œ ์—ฐ๊ฒฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Dispose() โ†’ null: ๊ฐœ์ฒด๋ฅผ ์™„์ „ํžˆ ๋‹ซ์Šต๋‹ˆ๋‹ค.
  • SendMessage(text, recipient) โ†’ string: ID ๋ฉ”์‹œ์ง€๋ฅผ ๋ฐ›์•˜์Šต๋‹ˆ๋‹ค. ๋งค๊ฐœ๋ณ€์ˆ˜ - ํ…์ŠคํŠธ ๋ฐ ๋ฌธ์ž์—ด ์ฑ„ํŒ… ID ํ…”๋ ˆ๊ทธ๋žจ, ์ฑ„๋„ ID Discord ๋˜๋Š” ID Viber ๊ฐ€์ž…์ž. ๊ธธ์ด ์ œํ•œ: ๊ฐ๊ฐ 4096/2000/7000; ๋˜ํ•œ Viber JSON โ‰ค30000 ๋ฐ”์ดํŠธ์ž…๋‹ˆ๋‹ค. ๋นˆ ํ…์ŠคํŠธ๋Š” ๊ธˆ์ง€๋ฉ๋‹ˆ๋‹ค. Discord๋Š” ๋ชจ๋“  ์‚ฌ๋žŒ/์—ญํ• ์— ๋Œ€ํ•œ ์–ธ๊ธ‰์„ ์ƒ์„ฑํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
  • WatchChannel(channelId) โ†’ null: Discord๋Š” ํ˜„์žฌ ๋งˆ์ง€๋ง‰ ๋ฉ”์‹œ์ง€๋ฅผ ๊ธฐ์–ตํ•˜๊ณ  ๋‹ค์Œ ๋ฉ”์‹œ์ง€๋งŒ ์ฝ์Šต๋‹ˆ๋‹ค. WatchChannel(channelId, afterMessageId) โ†’ null: ์ฃผ์–ด์ง„ ๋ฌธ์ž์—ด ID ๋‹ค์Œ์— ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค. '0'์—๋Š” ๊ธฐ๋ก์ด ํฌํ•จ๋ฉ๋‹ˆ๋‹ค. ๊ฐœ์ฒด๋‹น ์ตœ๋Œ€ 32 ์ฑ„๋„.
  • Receive(timeoutSeconds) โ†’ Array; Receive(): 0 ์‹œ๊ฐ„ ์ดˆ๊ณผ. ๋ฒ”์œ„ 0..20 ์ดˆ; ๋นˆ ๋ฐฐ์—ด์€ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ๋ฌธ์ž ๋ฉ”์‹œ์ง€๊ฐ€ ์—†์Œ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. Telegram์€ long ํด๋ง์„ ์‚ฌ์šฉํ•˜๊ณ , Discord๋Š” ์„ ํƒํ•œ ์ฑ„๋„์„ ํด๋งํ•˜๊ณ , Viber๋Š” ์›นํ›… ๋Œ€๊ธฐ์—ด์„ ์ฝ์Šต๋‹ˆ๋‹ค. ๋„คํŠธ์›Œํฌ ์š”์ฒญ์—๋Š” ์ตœ๋Œ€ 30์ดˆ๊ฐ€ ์†Œ์š”๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๊ฐ MessengerMessage: Id(), SenderId(), SenderName(), ChatId(), Text() โ†’ string. ์ฑ„๋„์˜ ํ…”๋ ˆ๊ทธ๋žจ ๊ฒŒ์‹œ๋ฌผ์—๋Š” SenderId์ด ์—†์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ฒจ๋ถ€ ํŒŒ์ผ, ํ…์ŠคํŠธ๊ฐ€ ์•„๋‹Œ ์ด๋ฒคํŠธ, ๋ด‡์˜ Discord ๋ฉ”์‹œ์ง€๋Š” ๋ฐ˜ํ™˜๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
  • RetryAfter() โ†’ int: ์„œ๋น„์Šค ์ œํ•œ ํ›„ ๋Œ€๊ธฐ ์‹œ๊ฐ„(์ดˆ)์ž…๋‹ˆ๋‹ค. ์ด ๊ธฐ๊ฐ„์ด ๋งŒ๋ฃŒ๋˜๊ธฐ ์ „์—๋Š” ์ƒˆ๋กœ์šด HTTP ์š”์ฒญ์ด ์‹คํ–‰๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์‹คํŒจ ํ›„ ์ „์†ก์€ ์ž๋™์œผ๋กœ ๋ฐ˜๋ณต๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์„œ๋ฒ„๊ฐ€ ์ด๋ฏธ ๋ฉ”์‹œ์ง€๋ฅผ ๋ฐ›์•˜์„ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.
  • StartReceiver(localPort, publicHttpsUrl) โ†’ int: Viber ์ „์šฉ; port 1..65535, ์‚ฌ์šฉ ์ค‘์ธ ๋กœ์ปฌ ํฌํŠธ๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ๊ณต๊ฐœ HTTPS-์ฃผ์†Œ๋Š” /viber/๋กœ ๋๋‚˜๊ณ  ๋ฏธ๋ฆฌ ์›๋ž˜ ๋ณธ๋ฌธ๊ณผ ์„œ๋ช… ํ—ค๋”๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ POST๋ฅผ 127.0.0.1:port/viber/๋กœ ์ „๋‹ฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด ๋ฉ”์„œ๋“œ๋Š” Viber์— ์›นํ›…์„ ๋“ฑ๋กํ•ฉ๋‹ˆ๋‹ค.
  • ๊ฐœ์ธ ๊ณ„์ • ๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ์•„๋‹Œ ๋ด‡ ํ† ํฐ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ๊ธฐ์กด ์›นํ›…์ด ์žˆ๋Š” ํ…”๋ ˆ๊ทธ๋žจ์€ ์ œ๊ฑฐํ•˜์ง€ ์•Š๊ณ  ๊ฑฐ๋ถ€๋ฉ๋‹ˆ๋‹ค. Discord์—๋Š” ์ฑ„๋„ ์ฝ๊ธฐ/์ „์†ก ๊ถŒํ•œ์ด ํ•„์š”ํ•˜๋ฉฐ ์•ก์„ธ์Šค ๊ฐ€๋Šฅํ•œ ํ…์ŠคํŠธ๋ฅผ ์œ„ํ•ด์„œ๋Š” MESSAGE_CONTENT๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. Viber๋Š” ๊ตฌ๋…ํ•œ ์‚ฌ์šฉ์ž์—๊ฒŒ ๋ณด๋ƒ…๋‹ˆ๋‹ค.
  • ์ˆ˜์‹ ์€ ์ „์ฒด ์Šคํƒ์„ ๊ตฌ๋ฌธ ๋ถ„์„ํ•œ ํ›„์—๋งŒ ์ปค์„œ๋ฅผ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค. Telegram/Discord๋Š” ์š”์ฒญ๋‹น ์ตœ๋Œ€ 100 ํ•ญ๋ชฉ์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ํ›„์† ํŒจํ‚ท์— ๋Œ€ํ•ด ๋‹ค์‹œ ์ˆ˜์‹ ์„ ํ˜ธ์ถœํ•˜์‹ญ์‹œ์˜ค. ๋‘ ๋ฒˆ์งธ ๋…ผ์Ÿ์ด ์—†๋Š” ๋ถˆ์ผ์น˜ WatchChannel๋Š” ์ด์ „ ์ด์•ผ๊ธฐ๋ฅผ ๊ฑด๋„ˆ๋œ๋‹ˆ๋‹ค.
  • Viber๋Š” ์†Œ์Šค ๋ณธ๋ฌธ์˜ HMAC-SHA256์„ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค. ๋Œ€๊ธฐ์—ด - 100 ๋ฉ”์‹œ์ง€, ๋ฐ˜๋ณต๋˜๋Š” ID๋Š” ๋งˆ์ง€๋ง‰ 2048 ์ „๋‹ฌ์— ๋Œ€ํ•ด ์ €์žฅ๋ฉ๋‹ˆ๋‹ค. ์˜ค๋ฒ„ํ”Œ๋กœ๋Š” ์„œ๋น„์Šค๋ณ„ ์žฌ์‹œ๋„๋ฅผ ์œ„ํ•ด HTTP 503๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. Content-Length๊ฐ€ ์žˆ๋Š” HTTPS ํ”„๋ก์‹œ๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ๋กœ์ปฌ ์ž…๋ ฅ์— ๋Œ€ํ•œ ์ฒญํฌ ์š”์ฒญ์€ ๊ฑฐ๋ถ€๋ฉ๋‹ˆ๋‹ค.
  • ํ† ํฐ์€ Connect ํ”„๋กœํ•„, ์˜ค๋ฅ˜ ํ…์ŠคํŠธ ๋˜๋Š” ์ธ์ˆ˜ ๊ธฐ๋ก์— ๊ธฐ๋ก๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋””๋ฒ„๊ฑฐ์— ํ‘œ์‹œ๋˜๋Š” ์Šคํฌ๋ฆฝํŠธ ๋ณ€์ˆ˜์— ์ €์žฅํ•˜์ง€ ์•Š์œผ๋ ค๋ฉด ConnectFromFile๋ฅผ ์‚ฌ์šฉํ•˜์„ธ์š”. ํ† ํฐ ํŒŒ์ผ์„ ๊ฒŒ์‹œํ•˜์ง€ ๋งˆ์‹ญ์‹œ์˜ค.
  • ํ”„๋กœ์‹œ์ € ๋‚ด๋ถ€์— ๊ฐœ์ฒด๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค. ๋™์‹œ์— 32 ์ดํ•˜; /Dispose๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋” ์ผ์ฐ ํ•ด์ œ๋ฉ๋‹ˆ๋‹ค. ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ์™„๋ฃŒ๋˜๊ฑฐ๋‚˜ ์‹คํŒจํ•˜๊ฑฐ๋‚˜ ์ค‘์ง€๋˜๋ฉด ๋ชจ๋“  ๋ฉ”์‹ ์ € ๊ฐœ์ฒด๊ฐ€ ๋‹ซํž™๋‹ˆ๋‹ค. Viber ์ˆ˜์‹ ๊ธฐ๋ฅผ ๋‹ซ์•„๋„ ์„œ๋น„์Šค์—์„œ ์›นํ›…์ด ์‚ญ์ œ๋˜์ง€๋Š” ์•Š์Šต๋‹ˆ๋‹ค.
  • ์ด๊ฒƒ์€ ํ…์ŠคํŠธ ์ธํ„ฐํŽ˜์ด์Šค์ž…๋‹ˆ๋‹ค. ์ˆ˜์‹ ๋œ ํ…์ŠคํŠธ๋ฅผ ์ฝ”๋“œ๋กœ ์‹คํ–‰ํ•˜์ง€ ์•Š์œผ๋ฉฐ ๊ฒŒ์ž„ ๊ณ„์ •์„ ์—ฐ๊ฒฐํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋ณด๋‚ธ ์‚ฌ๋žŒ์„ ์‹ ๋ขฐํ•˜๊ธฐ ์œ„ํ•œ ์กฐ๊ฑด์€ ์Šคํฌ๋ฆฝํŠธ ์ž์ฒด์—์„œ ์„ค์ •๋ฉ๋‹ˆ๋‹ค.

๋…๋ฆฝ ์‹คํ–‰ C# ์Šคํฌ๋ฆฝํŠธ

๊ฐ ์˜ˆ์ œ๋Š” ๋ณ„๋„์˜ ํŒŒ์ผ .csx๋กœ ์ €์žฅํ•˜๊ณ  IDE์—์„œ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. UO ๊ฐœ์ฒด์™€ ๋Œ€๊ธฐ ๊ธฐ๋Šฅ์€ ํ˜„์žฌ ๊ฒŒ์ž„ ์„ธ์…˜์˜ ํ˜ธ์ŠคํŠธ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ๋ฐ๋ชจ serial ์ด๋ฆ„, ์ขŒํ‘œ, ์ฃผ์†Œ๋ฅผ ์ž์‹ ์˜ ๊ฒƒ์œผ๋กœ ๋ฐ”๊พธ์„ธ์š”.

๊ฐ„๋‹จํ•œ ์˜ˆ์ œ

์ด ์ฝ”๋“œ๋ฅผ ๋ณ„๋„์˜ .csx ํŒŒ์ผ๋กœ ์‹คํ–‰ํ•˜์„ธ์š”. ์ธ์ˆ˜๋Š” ํ˜ธ์ถœ์— ์ง์ ‘ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค. ์˜ˆ์ œ์˜ ์ด๋ฆ„, serial, ์ฃผ์†Œ๋ฅผ ์ž์‹ ์˜ ๊ฐ’์œผ๋กœ ๋ฐ”๊พธ์„ธ์š”.

dynamic messenger = UO.Messenger("telegram");
UO.Print(messenger.Provider());
messenger.Dispose();

1. ํ…”๋ ˆ๊ทธ๋žจ: ์ž‘์—… ์™„๋ฃŒ ํ›„ ์•Œ๋ฆผ - ์ง์ ‘ ์Šคํฌ๋ฆฝํŠธ

๋งค๊ฐœ๋ณ€์ˆ˜์™€ ์‹คํ–‰ ํ๋ฆ„:

  • telegram-token.txt๋ฅผ ์Šคํฌ๋ฆฝํŠธ ์˜†์— ๋†“๊ณ  12345678๋ฅผ ์ฑ„ํŒ… ID๋กœ ๋ฐ”๊พธ์„ธ์š”. ์ด ์˜ˆ์—์„œ๋Š” ํ•˜๋‚˜์˜ ์•Œ๋ฆผ์„ ๋ณด๋‚ด๊ณ  ID ๋ฌธ์ž์—ด์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ์„œ๋ฒ„ ์‘๋‹ต์„ ์ˆ˜์‹ ํ•œ๋‹ค๊ณ  ํ•ด์„œ ์‚ฌ๋žŒ์ด ์ฝ์€ ๋‚ด์šฉ์„ ํ™•์ธํ•˜๋Š” ๊ฒƒ์€ ์•„๋‹™๋‹ˆ๋‹ค.
  • ์ž์‹ ๋งŒ์˜ ๋ด‡ ์„ค์ •์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ์ œ๋Š” ๋กœ์ปฌ ํ…Œ์ŠคํŠธ ๋‹ต๋ณ€์œผ๋กœ ํ…Œ์ŠคํŠธ๋ฉ๋‹ˆ๋‹ค. ์‹ค์ œ ์‚ฌ์šฉ์ž์—๊ฒŒ ์ „์†ก๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.
var bot = UO.Messenger("telegram");
using (var resource = bot)
{
    bot.ConnectFromFile("telegram-token.txt");
    var messageId = bot.SendMessage("Harvest finished", "12345678");
    return messageId;
}

2. Discord: ์„ ํƒํ•œ ์‚ฌ์šฉ์ž์˜ ์š”์ฒญ ์ค‘์ง€ - ์Šคํฌ๋ฆฝํŠธ ๊ธฐ๋Šฅ

๋งค๊ฐœ๋ณ€์ˆ˜์™€ ์‹คํ–‰ ํ๋ฆ„:

  • ์ฑ„๋„๊ณผ ID ํ—ˆ์šฉ๋œ ๋ฐœ์‹ ์ž๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”. true ์ด ์ˆ˜ํ‘œ๊ฐ€ ๊ทธ ์‚ฌ๋žŒ์œผ๋กœ๋ถ€ํ„ฐ ๋ฌธ์ž ์ค‘์ง€๋ฅผ ๋ฐ›์•˜๋‹ค๋Š” ์˜๋ฏธ์ž…๋‹ˆ๋‹ค. ์˜ˆ์ œ ์ž์ฒด๋Š” ์›€์ง์ž„์„ ๋ฉˆ์ถ”์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ด ๊ฒฐ๊ณผ๋Š” ๊ท€ํ•˜์˜ ๋“€ํ‹ฐ ์‚ฌ์ดํด ์กฐ๊ฑด์—์„œ ์‚ฌ์šฉ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์ž์‹ ๋งŒ์˜ ๋ด‡ ์„ค์ •์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ์ œ๋Š” ๋กœ์ปฌ ํ…Œ์ŠคํŠธ ๋‹ต๋ณ€์œผ๋กœ ํ…Œ์ŠคํŠธ๋ฉ๋‹ˆ๋‹ค. ์‹ค์ œ ์‚ฌ์šฉ์ž์—๊ฒŒ ์ „์†ก๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.
return  RunScenario();
 object? RunScenario()
{
    var bot = UO.Messenger("discord");
    using (var resource = bot)
    {
        bot.ConnectFromFile("discord-token.txt");
        bot.WatchChannel("123456789012345678");
        var messages = bot.Receive(5);
        foreach (var message in messages)
        {
            if (Truth(message.SenderId() == "987654321098765432") && Truth(Convert.ToString(TrimText(message.Text())) == "stop"))
            {
                return true;
            }
        }

        return false;
    }

    return null;
}

bool Truth(object? value) => value is bool flag ? flag : value is string text ? !string.IsNullOrEmpty(text) : value != null && Convert.ToDouble(value) != 0;
string TrimText(object? value) => (Convert.ToString(value) ?? "").Trim();

3. JSON ํŒŒ์ผ์—์„œ ์ˆ˜์‹ ๊ธฐ ์„ค์ • ๋กœ๋“œ - ์Šคํฌ๋ฆฝํŠธ ํด๋ž˜์Šค

๋งค๊ฐœ๋ณ€์ˆ˜์™€ ์‹คํ–‰ ํ๋ฆ„:

  • System.Text.Json์€ ๋กœ์ปฌ ํฌํŠธ/publicUrl๋ฅผ ์ฝ์Šต๋‹ˆ๋‹ค. ํ† ํฐ์€ ๋ณ„๋„์˜ ๋กœ์ปฌ ํŒŒ์ผ์— ์ €์žฅ๋ฉ๋‹ˆ๋‹ค. ์ด ์˜ˆ์—์„œ๋Š” ์‹ค์ œ ๋””์ŠคํŒจ์น˜๊ฐ€ ์ˆ˜ํ–‰๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ž„์˜์˜ ์ˆ˜์‹  ํ…์ŠคํŠธ๊ฐ€ ์•„๋‹Œ ์‹ ๋ขฐํ•  ์ˆ˜ ์žˆ๋Š” SenderId์ด ํ™•์ธ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.
return  new ScriptScenario(UO, WaitAsync, Args, Cancellation, Checkpoint).RunScenario();
class ScriptScenario
{
    private readonly global::ClassicUO.CSharp.CSharpGameApi UO;
    private readonly Func<int, Task> WaitAsync;
    private readonly string[] Args;
    private readonly CancellationToken Cancellation;
    private readonly Action Checkpoint;
    public ScriptScenario(global::ClassicUO.CSharp.CSharpGameApi api, Func<int, Task> wait, string[] args, CancellationToken cancellation, Action checkpoint)
    {
        UO = api;
        WaitAsync = wait;
        Args = args;
        Cancellation = cancellation;
        Checkpoint = checkpoint;
    }

     public object? RunScenario()
    {
        var settingsDocument = System.Text.Json.JsonDocument.Parse(System.IO.File.ReadAllText("viber-settings.json"));
        using (settingsDocument)
        {
            var settings = settingsDocument.RootElement;
            var bot = UO.Messenger("viber");
            using (var resource = bot)
            {
                bot.ConnectFromFile("viber-token.txt");
                bot.StartReceiver(settings.GetProperty("port").GetInt32(), settings.GetProperty("publicUrl").GetString());
                var messages = bot.Receive(5);
                foreach (var message in messages)
                {
                    if (Truth(message.SenderId() == "trusted-subscriber-id"))
                    {
                        return message.Text();
                    }
                }

                return "";
            }
        }

        return null;
    }

    bool Truth(object? value) => value is bool flag ? flag : value is string text ? !string.IsNullOrEmpty(text) : value != null && Convert.ToDouble(value) != 0;
}

๋ช…๋ น ๊ตฌํ˜„

C#์€ ๋Œ€์†Œ๋ฌธ์ž๋ฅผ ๊ตฌ๋ถ„ํ•ฉ๋‹ˆ๋‹ค. ์ •ํ™•ํ•œ UO ์ด๋ฆ„์„ ์‚ฌ์šฉํ•˜์„ธ์š”. ์ˆซ์ž ํ”Œ๋ž˜๊ทธ๋Š” 0๊ณผ ๋น„๊ตํ•˜๊ณ  ๋ฐ˜ํ™˜ ํ˜•์‹์ด bool์ผ ๋•Œ๋งŒ bool์„ ์‚ฌ์šฉํ•˜์„ธ์š”.

UO.Messenger(provider) โ€” UO.Messenger(String)

๋งค๊ฐœ๋ณ€์ˆ˜: provider (String)

๊ฒฐ๊ณผ: dynamic / Object.

ํ˜ธ์ถœ ์‹œ:

๋‚ด๋ถ€ ๊ตฌํ˜„ ์ฝ”๋“œ์ด๋ฉฐ ๋…๋ฆฝ ์Šคํฌ๋ฆฝํŠธ๋กœ ์‹คํ–‰ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

private InjectionObject CreateMessenger(string provider)
        {
            var owner=NativeScriptResourceScope.Current??throw new StatementFailedException("Create messenger objects inside an active C# script execution.");owner.Checkpoint();
            var client=MessengerFactory!=null?MessengerFactory(provider,()=>owner.Cancellation,owner.Checkpoint):new Messaging.MessengerClient(provider,()=>owner.Cancellation,owner.Checkpoint);
            var resource=new MessengerObject(client,owner.Forget,file=>Path.GetFullPath(Path.Combine(paths.ScriptPath??string.Empty,file)));owner.Track(resource);return resource;
        }
โš ๏ธ **GitHub.com Fallback** โš ๏ธ