From 5a01c2a3b0a055b1496f6f744c3faec23745cbc6 Mon Sep 17 00:00:00 2001 From: HaSa1002 Date: Sat, 31 Oct 2020 00:37:55 +0100 Subject: [PATCH] Docs: Port Code Examples to C# (F, G, H, I, J, K, L) Includes: * File * Geometry2D * HashingContext * HTTPClient * HTTPRequest * Image * Input * int * ItemList * JSONParseResult * KinematicBody2D * LineEdit Co-authored-by: Aaron Franke --- doc/classes/File.xml | 44 ++++++++++++++-- doc/classes/Geometry2D.xml | 13 ++++- doc/classes/HTTPClient.xml | 34 +++++++++--- doc/classes/HTTPRequest.xml | 92 +++++++++++++++++++++++++++++++-- doc/classes/HashingContext.xml | 36 +++++++++++-- doc/classes/Input.xml | 20 ++++--- doc/classes/ItemList.xml | 29 +++++++---- doc/classes/JSONParseResult.xml | 17 +++++- doc/classes/KinematicBody2D.xml | 13 ++++- doc/classes/LineEdit.xml | 12 ++++- doc/classes/int.xml | 22 ++++++-- 11 files changed, 289 insertions(+), 43 deletions(-) diff --git a/doc/classes/File.xml b/doc/classes/File.xml index ada57a81146..2f7ac551cf2 100644 --- a/doc/classes/File.xml +++ b/doc/classes/File.xml @@ -6,7 +6,8 @@ File type. This is used to permanently store data into the user device's file system and to read from it. This can be used to store game save data or player configuration files, for example. Here's a sample on how to write and read from a file: - [codeblock] + [codeblocks] + [gdscript] func save(content): var file = File.new() file.open("user://save_game.dat", File.WRITE) @@ -19,7 +20,26 @@ var content = file.get_as_text() file.close() return content - [/codeblock] + [/gdscript] + [csharp] + public void Save(string content) + { + var file = new File(); + file.Open("user://save_game.dat", File.ModeFlags.Write); + file.StoreString(content); + file.Close(); + } + + public string Load() + { + var file = new File(); + file.Open("user://save_game.dat", File.ModeFlags.Read); + string content = file.GetAsText(); + file.Close(); + return content; + } + [/csharp] + [/codeblocks] In the example above, the file will be saved in the user data folder as specified in the [url=https://docs.godotengine.org/en/latest/tutorials/io/data_paths.html]Data paths[/url] documentation. [b]Note:[/b] To access project resources once exported, it is recommended to use [ResourceLoader] instead of the [File] API, as some files are converted to engine-specific formats and their original source files might not be present in the exported PCK package. @@ -303,7 +323,8 @@ Stores an integer as 16 bits in the file. [b]Note:[/b] The [code]value[/code] should lie in the interval [code][0, 2^16 - 1][/code]. Any other value will overflow and wrap around. To store a signed integer, use [method store_64] or store a signed integer from the interval [code][-2^15, 2^15 - 1][/code] (i.e. keeping one bit for the signedness) and compute its sign manually when reading. For example: - [codeblock] + [codeblocks] + [gdscript] const MAX_15B = 1 << 15 const MAX_16B = 1 << 16 @@ -320,7 +341,22 @@ var read2 = f.get_16() # 121 var converted1 = unsigned16_to_signed(read1) # -42 var converted2 = unsigned16_to_signed(read2) # 121 - [/codeblock] + [/gdscript] + [csharp] + public override void _Ready() + { + var f = new File(); + f.Open("user://file.dat", File.ModeFlags.WriteRead); + f.Store16(unchecked((ushort)-42)); // This wraps around and stores 65494 (2^16 - 42). + f.Store16(121); // In bounds, will store 121. + f.Seek(0); // Go back to start to read the stored value. + ushort read1 = f.Get16(); // 65494 + ushort read2 = f.Get16(); // 121 + short converted1 = BitConverter.ToInt16(BitConverter.GetBytes(read1), 0); // -42 + short converted2 = BitConverter.ToInt16(BitConverter.GetBytes(read2), 0); // 121 + } + [/csharp] + [/codeblocks] diff --git a/doc/classes/Geometry2D.xml b/doc/classes/Geometry2D.xml index a6bcc1301b6..4ff54d15ce7 100644 --- a/doc/classes/Geometry2D.xml +++ b/doc/classes/Geometry2D.xml @@ -201,12 +201,21 @@ Each polygon's vertices will be rounded as determined by [code]join_type[/code], see [enum PolyJoinType]. The operation may result in an outer polygon (boundary) and inner polygon (hole) produced which could be distinguished by calling [method is_polygon_clockwise]. [b]Note:[/b] To translate the polygon's vertices specifically, multiply them to a [Transform2D]: - [codeblock] + [codeblocks] + [gdscript] var polygon = PackedVector2Array([Vector2(0, 0), Vector2(100, 0), Vector2(100, 100), Vector2(0, 100)]) var offset = Vector2(50, 50) polygon = Transform2D(0, offset) * polygon print(polygon) # prints [Vector2(50, 50), Vector2(150, 50), Vector2(150, 150), Vector2(50, 150)] - [/codeblock] + [/gdscript] + [csharp] + var polygon = new Vector2[] { new Vector2(0, 0), new Vector2(100, 0), new Vector2(100, 100), new Vector2(0, 100) }; + var offset = new Vector2(50, 50); + // TODO: This code is not valid right now. Ping @aaronfranke about it before Godot 4.0 is out. + //polygon = (Vector2[]) new Transform2D(0, offset).Xform(polygon); + //GD.Print(polygon); // prints [Vector2(50, 50), Vector2(150, 50), Vector2(150, 150), Vector2(50, 150)] + [/csharp] + [/codeblocks] diff --git a/doc/classes/HTTPClient.xml b/doc/classes/HTTPClient.xml index ec8ca7456a3..b6594aac397 100644 --- a/doc/classes/HTTPClient.xml +++ b/doc/classes/HTTPClient.xml @@ -112,17 +112,31 @@ Generates a GET/POST application/x-www-form-urlencoded style query string from a provided dictionary, e.g.: - [codeblock] + [codeblocks] + [gdscript] var fields = {"username": "user", "password": "pass"} var query_string = http_client.query_string_from_dict(fields) # Returns "username=user&password=pass" - [/codeblock] + [/gdscript] + [csharp] + var fields = new Godot.Collections.Dictionary { { "username", "user" }, { "password", "pass" } }; + string queryString = new HTTPClient().QueryStringFromDict(fields); + // Returns "username=user&password=pass" + [/csharp] + [/codeblocks] Furthermore, if a key has a [code]null[/code] value, only the key itself is added, without equal sign and value. If the value is an array, for each value in it a pair with the same key is added. - [codeblock] + [codeblocks] + [gdscript] var fields = {"single": 123, "not_valued": null, "multiple": [22, 33, 44]} var query_string = http_client.query_string_from_dict(fields) # Returns "single=123&not_valued&multiple=22&multiple=33&multiple=44" - [/codeblock] + [/gdscript] + [csharp] + var fields = new Godot.Collections.Dictionary{{"single", 123}, {"notValued", null}, {"multiple", new Godot.Collections.Array{22, 33, 44}}}; + string queryString = new HTTPClient().QueryStringFromDict(fields); + // Returns "single=123&not_valued&multiple=22&multiple=33&multiple=44" + [/csharp] + [/codeblocks] @@ -147,12 +161,20 @@ Sends a request to the connected host. The URL parameter is just the part after the host, so for [code]http://somehost.com/index.php[/code], it is [code]index.php[/code]. Headers are HTTP request headers. For available HTTP methods, see [enum Method]. To create a POST request with query strings to push to the server, do: - [codeblock] + [codeblocks] + [gdscript] var fields = {"username" : "user", "password" : "pass"} var query_string = http_client.query_string_from_dict(fields) var headers = ["Content-Type: application/x-www-form-urlencoded", "Content-Length: " + str(query_string.length())] var result = http_client.request(http_client.METHOD_POST, "index.php", headers, query_string) - [/codeblock] + [/gdscript] + [csharp] + var fields = new Godot.Collections.Dictionary { { "username", "user" }, { "password", "pass" } }; + string queryString = new HTTPClient().QueryStringFromDict(fields); + string[] headers = {"Content-Type: application/x-www-form-urlencoded", "Content-Length: " + queryString.Length}; + var result = new HTTPClient().Request(HTTPClient.Method.Post, "index.php", headers, queryString); + [/csharp] + [/codeblocks] [b]Note:[/b] The [code]request_data[/code] parameter is ignored if [code]method[/code] is [constant HTTPClient.METHOD_GET]. This is because GET methods can't contain request data. As a workaround, you can pass request data as a query string in the URL. See [method String.http_escape] for an example. diff --git a/doc/classes/HTTPRequest.xml b/doc/classes/HTTPRequest.xml index 6eae881ffe5..f2ab93033a5 100644 --- a/doc/classes/HTTPRequest.xml +++ b/doc/classes/HTTPRequest.xml @@ -7,7 +7,8 @@ A node with the ability to send HTTP requests. Uses [HTTPClient] internally. Can be used to make HTTP requests, i.e. download or upload files or web content via HTTP. [b]Example of contacting a REST API and printing one of its returned fields:[/b] - [codeblock] + [codeblocks] + [gdscript] func _ready(): # Create an HTTP request node and connect its completion signal. var http_request = HTTPRequest.new() @@ -34,9 +35,48 @@ # Will print the user agent string used by the HTTPRequest node (as recognized by httpbin.org). print(response.headers["User-Agent"]) - [/codeblock] + [/gdscript] + [csharp] + public override void _Ready() + { + // Create an HTTP request node and connect its completion signal. + var httpRequest = new HTTPRequest(); + AddChild(httpRequest); + httpRequest.Connect("request_completed", this, nameof(HttpRequestCompleted)); + + // Perform a GET request. The URL below returns JSON as of writing. + Error error = httpRequest.Request("https://httpbin.org/get"); + if (error != Error.Ok) + { + GD.PushError("An error occurred in the HTTP request."); + } + + // Perform a POST request. The URL below returns JSON as of writing. + // Note: Don't make simultaneous requests using a single HTTPRequest node. + // The snippet below is provided for reference only. + string[] body = { "name", "Godette" }; + // GDScript to_json is non existent, so we use JSON.Print() here. + error = httpRequest.Request("https://httpbin.org/post", null, true, HTTPClient.Method.Post, JSON.Print(body)); + if (error != Error.Ok) + { + GD.PushError("An error occurred in the HTTP request."); + } + } + + + // Called when the HTTP request is completed. + private void HttpRequestCompleted(int result, int response_code, string[] headers, byte[] body) + { + // GDScript parse_json is non existent so we have to use JSON.parse, which has a slightly different syntax. + var response = JSON.Parse(body.GetStringFromUTF8()).Result as Godot.Collections.Dictionary; + // Will print the user agent string used by the HTTPRequest node (as recognized by httpbin.org). + GD.Print((response["headers"] as Godot.Collections.Dictionary)["User-Agent"]); + } + [/csharp] + [/codeblocks] [b]Example of loading and displaying an image using HTTPRequest:[/b] - [codeblock] + [codeblocks] + [gdscript] func _ready(): # Create an HTTP request node and connect its completion signal. var http_request = HTTPRequest.new() @@ -51,6 +91,9 @@ # Called when the HTTP request is completed. func _http_request_completed(result, response_code, headers, body): + if result != HTTPRequest.RESULT_SUCCESS: + push_error("Image couldn't be downloaded. Try a different image.") + var image = Image.new() var error = image.load_png_from_buffer(body) if error != OK: @@ -63,7 +106,48 @@ var texture_rect = TextureRect.new() add_child(texture_rect) texture_rect.texture = texture - [/codeblock] + [/gdscript] + [csharp] + public override void _Ready() + { + // Create an HTTP request node and connect its completion signal. + var httpRequest = new HTTPRequest(); + AddChild(httpRequest); + httpRequest.Connect("request_completed", this, nameof(HttpRequestCompleted)); + + // Perform the HTTP request. The URL below returns a PNG image as of writing. + Error error = httpRequest.Request("https://via.placeholder.com/512"); + if (error != Error.Ok) + { + GD.PushError("An error occurred in the HTTP request."); + } + } + + + // Called when the HTTP request is completed. + private void HttpRequestCompleted(int result, int response_code, string[] headers, byte[] body) + { + if (result != (int)HTTPRequest.Result.Success) + { + GD.PushError("Image couldn't be downloaded. Try a different image."); + } + var image = new Image(); + Error error = image.LoadPngFromBuffer(body); + if (error != Error.Ok) + { + GD.PushError("Couldn't load the image."); + } + + var texture = new ImageTexture(); + texture.CreateFromImage(image); + + // Display the image in a TextureRect node. + var textureRect = new TextureRect(); + AddChild(textureRect); + textureRect.Texture = texture; + } + [/csharp] + [/codeblocks] [b]Gzipped response bodies[/b]: HTTPRequest will automatically handle decompression of response bodies. A [code]Accept-Encoding[/code] header will be automatically added to each of your requests, unless one is already specified. Any response with a [code]Content-Encoding: gzip[/code] header will automatically be decompressed and delivered to you as uncompressed bytes. [b]Note:[/b] When performing HTTP requests from a project exported to HTML5, keep in mind the remote server may not allow requests from foreign origins due to [url=https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS]CORS[/url]. If you host the server in question, you should modify its backend to allow requests from foreign origins by adding the [code]Access-Control-Allow-Origin: *[/code] HTTP header. diff --git a/doc/classes/HashingContext.xml b/doc/classes/HashingContext.xml index f8152c813ec..e020293d766 100644 --- a/doc/classes/HashingContext.xml +++ b/doc/classes/HashingContext.xml @@ -6,8 +6,9 @@ The HashingContext class provides an interface for computing cryptographic hashes over multiple iterations. This is useful for example when computing hashes of big files (so you don't have to load them all in memory), network streams, and data streams in general (so you don't have to hold buffers). The [enum HashType] enum shows the supported hashing algorithms. - [codeblock] - const CHUNK_SIZE = 1024 + [codeblocks] + [gdscript] + const CHUNK_SIZE = 102 func hash_file(path): var ctx = HashingContext.new() @@ -26,7 +27,36 @@ var res = ctx.finish() # Print the result as hex string and array. printt(res.hex_encode(), Array(res)) - [/codeblock] + [/gdscript] + [csharp] + public const int ChunkSize = 1024; + + public void HashFile(string path) + { + var ctx = new HashingContext(); + var file = new File(); + // Start a SHA-256 context. + ctx.Start(HashingContext.HashType.Sha256); + // Check that file exists. + if (!file.FileExists(path)) + { + return; + } + // Open the file to hash. + file.Open(path, File.ModeFlags.Read); + // Update the context after reading each chunk. + while (!file.EofReached()) + { + ctx.Update(file.GetBuffer(ChunkSize)); + } + // Get the computed hash. + byte[] res = ctx.Finish(); + // Print the result as hex string and array. + + GD.PrintT(res.HexEncode(), res); + } + [/csharp] + [/codeblocks] [b]Note:[/b] Not available in HTML5 exports. diff --git a/doc/classes/Input.xml b/doc/classes/Input.xml index 2e619802e39..5d1d763ae3e 100644 --- a/doc/classes/Input.xml +++ b/doc/classes/Input.xml @@ -299,12 +299,20 @@ Feeds an [InputEvent] to the game. Can be used to artificially trigger input events from code. Also generates [method Node._input] calls. Example: - [codeblock] - var a = InputEventAction.new() - a.action = "ui_cancel" - a.pressed = true - Input.parse_input_event(a) - [/codeblock] + [codeblocks] + [gdscript] + var cancel_event = InputEventAction.new() + cancel_event.action = "ui_cancel" + cancel_event.pressed = true + Input.parse_input_event(cancel_event) + [/gdscript] + [csharp] + var cancelEvent = new InputEventAction(); + cancelEvent.Action = "ui_cancel"; + cancelEvent.Pressed = true; + Input.ParseInputEvent(cancelEvent); + [/csharp] + [/codeblocks] diff --git a/doc/classes/ItemList.xml b/doc/classes/ItemList.xml index 25420bd77bf..fd8bfb4ecef 100644 --- a/doc/classes/ItemList.xml +++ b/doc/classes/ItemList.xml @@ -247,11 +247,16 @@ - Sets the background color of the item specified by [code]idx[/code] index to the specified [Color]. - [codeblock] - var some_string = "Some text" - some_string.set_item_custom_bg_color(0,Color(1, 0, 0, 1) # This will set the background color of the first item of the control to red. - [/codeblock] + [codeblocks] + [gdscript] + var itemList = ItemList.new() + some_string.set_item_custom_bg_color(0, Color.red) # This will set the background color of the first item of the control to red. + [/gdscript] + [csharp] + var itemList = new ItemList(); + itemList.SetItemCustomBgColor(0, Colors.Red); // This will set the background color of the first item of the control to red. + [/csharp] + [/codeblocks] @@ -263,10 +268,16 @@ Sets the foreground color of the item specified by [code]idx[/code] index to the specified [Color]. - [codeblock] - var some_string = "Some text" - some_string.set_item_custom_fg_color(0,Color(1, 0, 0, 1) # This will set the foreground color of the first item of the control to red. - [/codeblock] + [codeblocks] + [gdscript] + var item_list = ItemList.new() + item_list.set_item_custom_fg_color(0, Color.red) # This will set the foreground color of the first item of the control to red. + [/gdscript] + [csharp] + var itemList = new ItemList(); + itemList.SetItemCustomFgColor(0, Colors.Red); // This will set the foreground color of the first item of the control to red. + [/csharp] + [/codeblocks] diff --git a/doc/classes/JSONParseResult.xml b/doc/classes/JSONParseResult.xml index 4dbceb35e93..991ebcd7a08 100644 --- a/doc/classes/JSONParseResult.xml +++ b/doc/classes/JSONParseResult.xml @@ -24,13 +24,26 @@ A [Variant] containing the parsed JSON. Use [method @GDScript.typeof] or the [code]is[/code] keyword to check if it is what you expect. For example, if the JSON source starts with curly braces ([code]{}[/code]), a [Dictionary] will be returned. If the JSON source starts with brackets ([code][][/code]), an [Array] will be returned. [b]Note:[/b] The JSON specification does not define integer or float types, but only a [i]number[/i] type. Therefore, parsing a JSON text will convert all numerical values to [float] types. [b]Note:[/b] JSON objects do not preserve key order like Godot dictionaries, thus, you should not rely on keys being in a certain order if a dictionary is constructed from JSON. In contrast, JSON arrays retain the order of their elements: - [codeblock] + [codeblocks] + [gdscript] var p = JSON.parse('["hello", "world", "!"]') if typeof(p.result) == TYPE_ARRAY: print(p.result[0]) # Prints "hello" else: push_error("Unexpected results.") - [/codeblock] + [/gdscript] + [csharp] + JSONParseResult p = JSON.Parse("[\"hello\"], \"world\", \"!\"]"); + if (p.Result is Godot.Collections.Array) + { + GD.Print((p.Result as Godot.Collections.Array)[0]); // Prints "hello" + } + else + { + GD.PushError("Unexpected results."); + } + [/csharp] + [/codeblocks] diff --git a/doc/classes/KinematicBody2D.xml b/doc/classes/KinematicBody2D.xml index 425df00b6f2..476b64a336f 100644 --- a/doc/classes/KinematicBody2D.xml +++ b/doc/classes/KinematicBody2D.xml @@ -37,11 +37,20 @@ Returns a [KinematicCollision2D], which contains information about a collision that occurred during the last call to [method move_and_slide] or [method move_and_slide_with_snap]. Since the body can collide several times in a single call to [method move_and_slide], you must specify the index of the collision in the range 0 to ([method get_slide_count] - 1). [b]Example usage:[/b] - [codeblock] + [codeblocks] + [gdscript] for i in get_slide_count(): var collision = get_slide_collision(i) print("Collided with: ", collision.collider.name) - [/codeblock] + [/gdscript] + [csharp] + for (int i = 0; i < GetSlideCount(); i++) + { + KinematicCollision2D collision = GetSlideCollision(i); + GD.Print("Collided with: ", (collision.Collider as Node).Name); + } + [/csharp] + [/codeblocks] diff --git a/doc/classes/LineEdit.xml b/doc/classes/LineEdit.xml index 5c2dffd5383..b7211419e43 100644 --- a/doc/classes/LineEdit.xml +++ b/doc/classes/LineEdit.xml @@ -103,12 +103,20 @@ Selects characters inside [LineEdit] between [code]from[/code] and [code]to[/code]. By default, [code]from[/code] is at the beginning and [code]to[/code] at the end. - [codeblock] + [codeblocks] + [gdscript] text = "Welcome" select() # Will select "Welcome". select(4) # Will select "ome". select(2, 5) # Will select "lco". - [/codeblock] + [/gdscript] + [csharp] + Text = "Welcome"; + Select(); // Will select "Welcome". + Select(4); // Will select "ome". + Select(2, 5); // Will select "lco". + [/csharp] + [/codeblocks] diff --git a/doc/classes/int.xml b/doc/classes/int.xml index 5ac9f8405a9..b0d0a4bd7bc 100644 --- a/doc/classes/int.xml +++ b/doc/classes/int.xml @@ -7,18 +7,34 @@ Signed 64-bit integer type. It can take values in the interval [code][-2^63, 2^63 - 1][/code], i.e. [code][-9223372036854775808, 9223372036854775807][/code]. Exceeding those bounds will wrap around. [int] is a [Variant] type, and will thus be used when assigning an integer value to a [Variant]. It can also be enforced with the [code]: int[/code] type hint. - [codeblock] + [codeblocks] + [gdscript] var my_variant = 0 # int, value 0. my_variant += 4.2 # float, value 4.2. var my_int: int = 1 # int, value 1. my_int = 4.2 # int, value 4, the right value is implicitly cast to int. my_int = int("6.7") # int, value 6, the String is explicitly cast with int. - var max_int = 9223372036854775807 print(max_int) # 9223372036854775807, OK. max_int += 1 print(max_int) # -9223372036854775808, we overflowed and wrapped around. - [/codeblock] + [/gdscript] + [csharp] + int myInt = (int)"6.7".ToFloat(); // int, value 6, the String is explicitly cast with int. + // We have to use `long` here, because GDSript's `int` + // is 64 bits long while C#'s `int` is only 32 bits. + long maxInt = 9223372036854775807; + GD.Print(maxInt); // 9223372036854775807, OK. + maxInt++; + GD.Print(maxInt); // -9223372036854775808, we overflowed and wrapped around. + + // Alternatively, if we used C#'s 32-bit `int` type, the maximum value is much smaller: + int halfInt = 2147483647; + GD.Print(halfInt); // 2147483647, OK. + halfInt++; + GD.Print(halfInt); // -2147483648, we overflowed and wrapped around. + [/csharp] + [/codeblocks]