diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 10fe6080..9ef3227c 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -93,14 +93,23 @@ jobs: - name: Build Burrito Link run: | + mkdir burrito_link/build cd burrito_link + cmake .. make - - name: Upload created file + - name: Upload Standalone Executable Burrito Link + uses: actions/upload-artifact@v3 + with: + name: burrito_link_exe + path: burrito_link/build/burrito_link.exe + if-no-files-found: error + + - name: Upload Shared Library Burrito Link uses: actions/upload-artifact@v3 with: - name: burrito_link - path: burrito_link/burrito_link.exe + name: burrito_link_dll + path: burrito_link/build/d3d11.dll if-no-files-found: error Build-BurritoFG-Linux: @@ -228,15 +237,22 @@ jobs: with: name: xml_converter - - name: Download Burrito Link + - name: Download Burrito Link Exe uses: actions/download-artifact@v3 with: - name: burrito_link + name: burrito_link_exe + + - name: Download Burrito Link DLL + uses: actions/download-artifact@v3 + with: + name: burrito_link_dll + - name: Move Burrito Link run: | mkdir burrito_link/ mv burrito_link.exe burrito_link/ + mv d3d11.dll burrito_link/ - uses: actions/upload-artifact@v3 with: diff --git a/Gizmo/PointEdit.gd b/Gizmo/PointEdit.gd index ea6769ba..93ddfa5c 100644 --- a/Gizmo/PointEdit.gd +++ b/Gizmo/PointEdit.gd @@ -69,5 +69,5 @@ func _process(delta): ) $Plane.scale = new_scale $Pillar.scale = new_scale - + update_point() diff --git a/Route.gd b/Route.gd index 07a87187..392b2ffd 100644 --- a/Route.gd +++ b/Route.gd @@ -16,7 +16,7 @@ func refresh_mesh(): var tmpMesh = Mesh.new() var i = 0 var last_uv: float = 0.0 - for point_index in range(len(point_list)-1): + for point_index in range(len(point_list)-1): var point:Vector3 = point_list[point_index] var next_point:Vector3 = point_list[point_index+1] # If the line starts or ends at map coordinates (0,0,0), don't draw the line. @@ -56,7 +56,7 @@ func refresh_mesh(): var st = SurfaceTool.new() st.begin(Mesh.PRIMITIVE_TRIANGLE_FAN) - for v in vertices.size(): + for v in vertices.size(): st.add_color(color) st.add_uv(UVs[v]) st.add_vertex(vertices[v]) diff --git a/Settings.gd b/Settings.gd index f163a5ed..2af0fb9b 100644 --- a/Settings.gd +++ b/Settings.gd @@ -5,6 +5,9 @@ const CONFIG_PATH = "user://settings.json" var _config_data = {} var local_category_data = {} +var minimum_width: int = 800 +var minimum_height: int = 600 + var override_size_enabled: bool = false; var override_size_height: int = 1080 var override_size_width: int = 1920 @@ -18,7 +21,7 @@ func _ready(): var file = File.new() file.open(CONFIG_PATH, file.READ) var text = file.get_as_text() - var datum = JSON.parse(text) + var datum = JSON.parse(text) self._config_data = JSON.parse(text).result if self._config_data == null: @@ -26,6 +29,10 @@ func _ready(): if "local_category_data" in self._config_data: self.local_category_data = self._config_data["local_category_data"] + if "minimum_width" in self._config_data: + self.minimum_width = self._config_data["minimum_width"] + if "minimum_height" in self._config_data: + self.minimum_height = self._config_data["minimum_height"] if "override_size_enabled" in self._config_data: self.override_size_enabled = self._config_data["override_size_enabled"] if "override_size_height" in self._config_data: @@ -42,6 +49,8 @@ func _ready(): func save(): _config_data = { + "minimum_width": minimum_width, + "minimum_height": minimum_height, "override_size_enabled": override_size_enabled, "override_size_height": override_size_height, "override_size_width": override_size_width, @@ -50,7 +59,7 @@ func save(): "burrito_link_env_args": burrito_link_env_args, "local_category_data": local_category_data } - + var file = File.new() file.open(CONFIG_PATH, File.WRITE) file.store_string(JSON.print(self._config_data, " ")) diff --git a/SettingsDialog.gd b/SettingsDialog.gd index b79c0129..a78b0458 100644 --- a/SettingsDialog.gd +++ b/SettingsDialog.gd @@ -1,34 +1,44 @@ extends WindowDialog func load_settings(): - var override_size: CheckButton = $GridContainer/OverrideSize + var minimum_width: LineEdit = $ScrollContainer/GridContainer/MinimumWidth + minimum_width.text = String(Settings.minimum_width) + var minimum_height: LineEdit = $ScrollContainer/GridContainer/MinimumHeight + minimum_height.text = String(Settings.minimum_height) + + var override_size: CheckButton = $ScrollContainer/GridContainer/OverrideSize override_size.pressed = Settings.override_size_enabled - var override_height: LineEdit = $GridContainer/OverrideHeight + var override_height: LineEdit = $ScrollContainer/GridContainer/OverrideHeight override_height.text = String(Settings.override_size_height) - var override_width: LineEdit = $GridContainer/OverrideWidth + var override_width: LineEdit = $ScrollContainer/GridContainer/OverrideWidth override_width.text = String(Settings.override_size_width) - var autolaunch_burrito_link: CheckButton = $GridContainer/AutoLaunchBurritoLink + var autolaunch_burrito_link: CheckButton = $ScrollContainer/GridContainer/AutoLaunchBurritoLink autolaunch_burrito_link.pressed = Settings.burrito_link_auto_launch_enabled - var wine_path: LineEdit = $GridContainer/WinePath + var wine_path: LineEdit = $ScrollContainer/GridContainer/WinePath wine_path.text = Settings.burrito_link_wine_path - var environment_vars: TextEdit = $GridContainer/EnvironmentVars + var environment_vars: TextEdit = $ScrollContainer/GridContainer/EnvironmentVars environment_vars.text = Settings.burrito_link_env_args func save_settings(new_value=null): - var override_size: CheckButton = $GridContainer/OverrideSize + var minimum_width: LineEdit = $ScrollContainer/GridContainer/MinimumWidth + Settings.minimum_width = int(minimum_width.text) + var minimum_height: LineEdit = $ScrollContainer/GridContainer/MinimumHeight + Settings.minimum_height = int(minimum_height.text) + + var override_size: CheckButton = $ScrollContainer/GridContainer/OverrideSize Settings.override_size_enabled = override_size.pressed - var override_height: LineEdit = $GridContainer/OverrideHeight + var override_height: LineEdit = $ScrollContainer/GridContainer/OverrideHeight Settings.override_size_height = int(override_height.text) - var override_width: LineEdit = $GridContainer/OverrideWidth + var override_width: LineEdit = $ScrollContainer/GridContainer/OverrideWidth Settings.override_size_width = int(override_width.text) - var autolaunch_burrito_link: CheckButton = $GridContainer/AutoLaunchBurritoLink + var autolaunch_burrito_link: CheckButton = $ScrollContainer/GridContainer/AutoLaunchBurritoLink Settings.burrito_link_auto_launch_enabled = autolaunch_burrito_link.pressed - var wine_path: LineEdit = $GridContainer/WinePath + var wine_path: LineEdit = $ScrollContainer/GridContainer/WinePath Settings.burrito_link_wine_path = wine_path.text - var environment_vars: TextEdit = $GridContainer/EnvironmentVars + var environment_vars: TextEdit = $ScrollContainer/GridContainer/EnvironmentVars Settings.burrito_link_env_args = environment_vars.text - + Settings.save() diff --git a/Spatial.gd b/Spatial.gd index 60c48d98..5a030f9c 100644 --- a/Spatial.gd +++ b/Spatial.gd @@ -28,7 +28,7 @@ var map_was_open = false var player_position := Vector3(0,0,0) # Player Position as accurate to Godot (z value sign is flipped) -var correct_player_position := Vector3(0,0,0) +var correct_player_position := Vector3(0,0,0) var compass_height: int = 0; var compass_width: int = 0; @@ -36,7 +36,7 @@ var compass_width: int = 0; # A temporary setting able to be configured by the user. It is used to allow # for faster trail mesh generation. The higher the value the fewer samples are -# taken for the MeshCSG leading to an overall lower number of polygons. +# taken for the MeshCSG leading to an overall lower number of polygons. var path_resolution = 1 # Variables that store opposit corners of the compass @@ -56,20 +56,78 @@ func _ready(): x11_window_id_burrito = OS.get_native_handle(OS.WINDOW_HANDLE) OS.window_maximized = false # Start off with a small size before GW2 client is up - OS.window_size = Vector2(800, 600) + + if Settings.override_size_enabled: + OS.window_size = Vector2(Settings.override_size_width, Settings.override_size_height) + else: + OS.window_size = Vector2(Settings.minimum_width, Settings.minimum_height) # Postion at top left corner OS.set_window_position(Vector2(0,0)) set_minimal_mouse_block() server.listen(4242) + if (Settings.burrito_link_auto_launch_enabled): + launch_burrito_link() + + +################################################################################ +# show_error +# +# This function prints a user error out. Currently it prints to stdout but may +# one day be shown to the user. +################################################################################ +func show_user_error(error_string: String): + print(error_string) + + +# The process id of burrito link if it is launched automatically by burrito +var burrito_link_process_id = 0 + +################################################################################ +# launch_burrito_link +# +# This function launches the burrito link binary using the values for it that +# are saved in "settings". +################################################################################ +func launch_burrito_link(): + for env_arg in Settings.burrito_link_env_args.split("\n"): + env_arg = env_arg.trim_prefix("export ") + var key_values = env_arg.split('=', true, 1) + + if len(key_values) != 2: + show_user_error("Invalid burrito_link environment arg: " + env_arg) + return + + var key = key_values[0] + var value = key_values[1].trim_prefix('"').trim_suffix('"') + OS.set_environment(key, value) + + # Launch burrito link with a 2 hour timeout + # If burrito crashes then burrito_link will automatically exit at the timeout. + # If burrito does not crash and the timeout expires then burrito will relaunch burrito_link automatically + burrito_link_process_id = OS.execute(Settings.burrito_link_wine_path, ["burrito_link/burrito_link.exe", "--timeout", "7200"], false) + +func close_burrito_link(): + if (burrito_link_process_id != 0): + OS.kill(burrito_link_process_id) + burrito_link_process_id = 0 + + +func exit_burrito(): + if Settings.burrito_link_auto_launch_enabled: + close_burrito_link() + get_tree().quit() + + func set_minimal_mouse_block(): - var top_corner := Vector2(287, 0) - var bottom_corner := Vector2(314, 32) - + var menu_button = $Control/GlobalMenuButton + var top_corner = menu_button.get_position() + var bottom_corner = menu_button.get_position() + menu_button.get_size() + if self.edit_panel_open: - bottom_corner.y = 49 - bottom_corner.x = 314+377 - + var editor_panel = $Control/GlobalMenuButton/EditorQuckPanel + bottom_corner = menu_button.get_position() + editor_panel.get_position() + editor_panel.get_size() + var clickthrough: PoolVector2Array = [ Vector2(top_corner.x ,top_corner.y), Vector2(bottom_corner.x, top_corner.y), @@ -85,7 +143,7 @@ func set_maximal_mouse_block(): # Called every frame. 'delta' is the elapsed time since the previous frame. func _process(delta): - #OS.window_position = Vector2(1920, 0) # TODO: This does not seem to work + #OS.window_position = Vector2(1920, 0) # TODO: This does not seem to work #OS.set_window_position(Vector2(1920,0)) #print(OS.window_position) server.poll() # Important @@ -106,7 +164,9 @@ func _process(delta): if packet_type == 1: decode_frame_packet(spb) elif packet_type == 2: - decode_context_packet(spb) + decode_context_packet(spb) + elif packet_type == 3: + decode_timeout_packet(spb) return @@ -117,14 +177,14 @@ func decode_frame_packet(spb: StreamPeerBuffer): spb.get_float(), spb.get_float() ) - + # Extract the rotation of the camera in the form of a normal vector var camera_facing = Vector3( spb.get_float(), spb.get_float(), spb.get_float() ) - + # Extract the position of the player's foot self.player_position = Vector3( spb.get_float(), @@ -132,10 +192,10 @@ func decode_frame_packet(spb: StreamPeerBuffer): spb.get_float() ) self.correct_player_position = Vector3(player_position.x, player_position.y, -player_position.z) - + if $Control/Position.visible: $Control/Position.text = "X " + str(player_position.x) + " Y " + str(player_position.y) + " Z " + str(-player_position.z) - + var map_offset = Vector2( spb.get_float(), spb.get_float() @@ -145,7 +205,7 @@ func decode_frame_packet(spb: StreamPeerBuffer): map_scale = 0.000001 var map_rotation: float = spb.get_float() var ui_flags: int = spb.get_32() - + map_is_open = (ui_flags & 0x01) == 0x01; compass_is_top_right = (ui_flags & 0x02) == 0x02; var compass_rotation_is_enabled: bool = (ui_flags & 0x04) == 0x04; @@ -168,7 +228,7 @@ func decode_frame_packet(spb: StreamPeerBuffer): $CameraMount.translation.x = camera_position.x $CameraMount.translation.y = camera_position.y $CameraMount.translation.z = -camera_position.z - + # Orent the camera in the same rotation as it is facing in game $CameraMount/Camera.rotation.x = asin(camera_facing.y) $CameraMount.rotation.y = -atan2(camera_facing.x, camera_facing.z) @@ -184,7 +244,7 @@ func decode_frame_packet(spb: StreamPeerBuffer): var map_size = get_viewport().size var map_corner = Vector2(0, 0) - + if (!map_is_open): map_size = Vector2(compass_width, compass_height) if !compass_is_top_right: @@ -204,16 +264,16 @@ func decode_frame_packet(spb: StreamPeerBuffer): var x = (cosTheta * (player_map_position.x - pivot.x) - sinTheta * (player_map_position.y - pivot.y) + pivot.x); var y = (sinTheta * (player_map_position.x - pivot.x) + cosTheta * (player_map_position.y - pivot.y) + pivot.y); - + delta_position = player_map_position - Vector2(x, y); - + #print(map_rotation) $Control/MiniMap.rotation = map_rotation else: $Control/MiniMap.rotation = 0 - + var map_midpoint = map_size/2 + map_corner; - + $Control/MiniMap.scale=Vector2(map_object_scaling, map_object_scaling) var map_translation = map_offset $Control/MiniMap.position = (map_translation / map_scale) + map_midpoint - player_map_position + delta_position @@ -231,7 +291,7 @@ func decode_context_packet(spb: StreamPeerBuffer): if !is_transient: is_transient = x11_fg.set_transient_for(x11_window_id_burrito, x11_window_id_gw2) - var size = Vector2(800, 600) + var size = Vector2(Settings.minimum_width, Settings.minimum_height) if Settings.override_size_enabled: size.x = Settings.override_size_width size.y = Settings.override_size_height @@ -239,11 +299,17 @@ func decode_context_packet(spb: StreamPeerBuffer): var size_tuple = x11_fg.get_window_geometry(x11_window_id_gw2) size.x = size_tuple[0] size.y = size_tuple[1] + + if size.x < Settings.minimum_width: + size.x = Settings.minimum_width + if size.y < Settings.minimum_height: + size.y = Settings.minimum_height + OS.window_size = size var identity_length: int = spb.get_32() var identity_str = spb.get_utf8_string(identity_length) var identity = JSON.parse(identity_str).result - + # FOV Calculations # The minimum value on the FOV slider gives a float value in this field of 0.436 # The maximum value on the FOV slider gives a float value in this field of 1.222 @@ -255,6 +321,10 @@ func decode_context_packet(spb: StreamPeerBuffer): # this to just be a radian to degree conversion. if self.map_id != old_map_id: + print("New Map") + + print("Saving Old Map") + self.markerdata[str(old_map_id)] = data_from_renderview() print("Loading New Map") load_waypoint_markers(self.map_id) gen_map_markers() @@ -269,6 +339,14 @@ func decode_context_packet(spb: StreamPeerBuffer): reset_minimap_masks() + +func decode_timeout_packet(spb: StreamPeerBuffer): + if Settings.burrito_link_auto_launch_enabled: + print("Link Timeout Reached, should restart link if started by burrito automatically") + close_burrito_link() + launch_burrito_link() + + func reset_minimap_masks(): var viewport_size = get_viewport().size compass_corner1 = Vector2(0, 0) @@ -279,7 +357,7 @@ func reset_minimap_masks(): elif !map_is_open && compass_is_top_right: compass_corner1 = viewport_size - Vector2(compass_width, compass_height) compass_corner2 = compass_corner1 + Vector2(compass_width, compass_height) - + for minimap_path in $Control/MiniMap.get_children(): minimap_path.material.set_shader_param("minimap_corner", compass_corner1) minimap_path.material.set_shader_param("minimap_corner2", compass_corner2) @@ -377,8 +455,8 @@ func _unhandled_input(event): if is_instance_valid(self.last_hover) and self.last_hover.has_method("unhover"): self.last_hover.unhover() self.last_hover = null - - + + ################################################################################ # ################################################################################ @@ -448,6 +526,7 @@ func search_category_tree(split_name: PoolStringArray, category_item: TreeItem, print("No category found for ", split_name) return null + func build_category_tree(): var root = self.marker_packs.create_item() root.set_text(0, "Markers available on current map") @@ -528,7 +607,7 @@ func gen_new_path(points: Array, texture_path: String, waypoint_trail, category_ # Create the texture to use from an image file # TODO: We want to be able to cache this data so that if a texture is used # by multiple objects we only need to keep ony copy of it in memory. #22. - # TODO: We want to have two copies of each texture in memory one for 2D + # TODO: We want to have two copies of each texture in memory one for 2D # which does not use srgb to render properly, and one for 3D which forces # srgb to render properly. Issue #23. var texture_file = File.new() @@ -545,11 +624,11 @@ func gen_new_path(points: Array, texture_path: String, waypoint_trail, category_ var new_route = route_scene.instance() new_route.texture_path = texture_path # Save the location of the image for later - + var points_3d := PoolVector3Array() for point in points: points_3d.append(Vector3(point[0], point[1], -point[2])) - + new_route.create_mesh(points_3d) new_route.set_texture(texture) new_route.waypoint = waypoint_trail @@ -559,7 +638,7 @@ func gen_new_path(points: Array, texture_path: String, waypoint_trail, category_ new_route.visible = false paths.add_child(new_route) - + # Create a new 2D Path var new_2d_path = path2d_scene.instance() var points_2d := PoolVector2Array() @@ -570,6 +649,9 @@ func gen_new_path(points: Array, texture_path: String, waypoint_trail, category_ new_2d_path.visible = new_route.visible minimap.add_child(new_2d_path) + self.currently_active_path = new_route + self.currently_active_path_2d = new_2d_path + ################################################################################ # @@ -591,13 +673,13 @@ func gen_new_icon(position: Vector3, texture_path: String, waypoint_icon, catego func data_from_renderview(): var icons_data = [] var paths_data = [] - + for icon in $Icons.get_children(): icons_data.append({ "position": [icon.translation.x, icon.translation.y, -icon.translation.z], "texture": icon.texture_path }) - + for path in $Paths.get_children(): #print(path) var points = [] @@ -825,14 +907,14 @@ func _on_NewNodeAfter_pressed(): if path.get_point_count() > index+1: var end = path.get_point_position(index+1) midpoint = ((start-end)/2) + end - + path.add_point(midpoint, index+1) path2d.add_point(Vector2(midpoint.x, midpoint.z), index+1) clear_adjustment_nodes() gen_adjustment_nodes() on_gizmo_deselected(self.currently_selected_node) - + func _on_XZSnapToPlayer_pressed(): self.currently_selected_node.translation.x = self.player_position.x self.currently_selected_node.translation.z = -self.player_position.z @@ -860,7 +942,7 @@ func _on_ReversePathDirection_pressed(): func _on_ExitButton_pressed(): - get_tree().quit() + exit_burrito() func _on_Settings_pressed(): diff --git a/Spatial.tscn b/Spatial.tscn index 5db276b4..fc282777 100644 --- a/Spatial.tscn +++ b/Spatial.tscn @@ -71,8 +71,8 @@ scale = Vector2( 2, 2 ) [node name="Line2D" parent="Control/MiniMap" instance=ExtResource( 4 )] [node name="GlobalMenuButton" type="Control" parent="Control"] -margin_left = 287.348 -margin_right = 314.348 +margin_left = 323.0 +margin_right = 349.0 margin_bottom = 32.0 __meta__ = { "_edit_use_anchors_": false @@ -80,10 +80,9 @@ __meta__ = { [node name="TextureRect" type="TextureRect" parent="Control/GlobalMenuButton"] modulate = Color( 1, 1, 1, 0.439216 ) -margin_left = 1.591 -margin_top = 3.18198 -margin_right = 26.591 -margin_bottom = 28.182 +margin_top = 4.0 +margin_right = 25.0 +margin_bottom = 29.0 texture = ExtResource( 3 ) __meta__ = { "_edit_use_anchors_": false @@ -91,6 +90,7 @@ __meta__ = { [node name="main_menu_toggle" type="Button" parent="Control/GlobalMenuButton"] modulate = Color( 1, 1, 1, 0 ) +margin_left = -2.0 margin_right = 27.0 margin_bottom = 32.0 __meta__ = { @@ -101,7 +101,7 @@ __meta__ = { visible = false margin_left = 32.0 margin_top = -2.0 -margin_right = 306.0 +margin_right = 342.0 margin_bottom = 52.0 __meta__ = { "_edit_use_anchors_": false @@ -199,6 +199,7 @@ margin_bottom = 715.0 window_title = "Main Menu" resizable = true __meta__ = { +"_edit_group_": true, "_edit_use_anchors_": false } @@ -600,125 +601,158 @@ disabled = true text = "Reverse Path Direction" [node name="SettingsDialog" type="WindowDialog" parent="Control/Dialogs"] +visible = true margin_left = 592.0 margin_top = 146.0 margin_right = 981.0 margin_bottom = 575.0 window_title = "Settings" +resizable = true script = ExtResource( 12 ) __meta__ = { "_edit_use_anchors_": false } -[node name="GridContainer" type="GridContainer" parent="Control/Dialogs/SettingsDialog"] +[node name="ScrollContainer" type="ScrollContainer" parent="Control/Dialogs/SettingsDialog"] anchor_right = 1.0 anchor_bottom = 1.0 -margin_left = 10.0 -margin_top = 10.0 -margin_right = -10.0 -margin_bottom = -10.0 +margin_left = 5.0 +margin_top = 5.0 +margin_bottom = -5.0 +scroll_horizontal_enabled = false +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="GridContainer" type="GridContainer" parent="Control/Dialogs/SettingsDialog/ScrollContainer"] +margin_right = 384.0 +margin_bottom = 419.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 columns = 2 __meta__ = { "_edit_use_anchors_": false } -[node name="OverrideSizeLabel" type="Label" parent="Control/Dialogs/SettingsDialog/GridContainer"] -margin_top = 13.0 -margin_right = 102.0 -margin_bottom = 27.0 -text = "Override Size" +[node name="MinimumWidthLabel" type="Label" parent="Control/Dialogs/SettingsDialog/ScrollContainer/GridContainer"] +margin_top = 5.0 +margin_right = 112.0 +margin_bottom = 19.0 +text = "Minimum Width" -[node name="OverrideSize" type="CheckButton" parent="Control/Dialogs/SettingsDialog/GridContainer"] -margin_left = 106.0 -margin_right = 369.0 -margin_bottom = 40.0 +[node name="MinimumWidth" type="LineEdit" parent="Control/Dialogs/SettingsDialog/ScrollContainer/GridContainer"] +margin_left = 116.0 +margin_right = 384.0 +margin_bottom = 24.0 size_flags_horizontal = 3 -[node name="OverrideWidthLabel" type="Label" parent="Control/Dialogs/SettingsDialog/GridContainer"] -margin_top = 49.0 -margin_right = 102.0 -margin_bottom = 63.0 -text = "Override Width" +[node name="MinimumHeightLabel" type="Label" parent="Control/Dialogs/SettingsDialog/ScrollContainer/GridContainer"] +margin_top = 33.0 +margin_right = 112.0 +margin_bottom = 47.0 +text = "Minimum Height" -[node name="OverrideWidth" type="LineEdit" parent="Control/Dialogs/SettingsDialog/GridContainer"] -margin_left = 106.0 -margin_top = 44.0 -margin_right = 369.0 -margin_bottom = 68.0 +[node name="MinimumHeight" type="LineEdit" parent="Control/Dialogs/SettingsDialog/ScrollContainer/GridContainer"] +margin_left = 116.0 +margin_top = 28.0 +margin_right = 384.0 +margin_bottom = 52.0 size_flags_horizontal = 3 -[node name="OverrideHeightLabel" type="Label" parent="Control/Dialogs/SettingsDialog/GridContainer"] -margin_top = 77.0 -margin_right = 102.0 -margin_bottom = 91.0 -text = "Override Height" +[node name="OverrideSizeLabel" type="Label" parent="Control/Dialogs/SettingsDialog/ScrollContainer/GridContainer"] +margin_top = 69.0 +margin_right = 112.0 +margin_bottom = 83.0 +text = "Use Fixed Size" -[node name="OverrideHeight" type="LineEdit" parent="Control/Dialogs/SettingsDialog/GridContainer"] -margin_left = 106.0 -margin_top = 72.0 -margin_right = 369.0 +[node name="OverrideSize" type="CheckButton" parent="Control/Dialogs/SettingsDialog/ScrollContainer/GridContainer"] +margin_left = 116.0 +margin_top = 56.0 +margin_right = 384.0 margin_bottom = 96.0 size_flags_horizontal = 3 -[node name="HSeparator" type="HSeparator" parent="Control/Dialogs/SettingsDialog/GridContainer"] -visible = false +[node name="OverrideWidthLabel" type="Label" parent="Control/Dialogs/SettingsDialog/ScrollContainer/GridContainer"] +margin_top = 105.0 +margin_right = 112.0 +margin_bottom = 119.0 +text = "Fixed Width" + +[node name="OverrideWidth" type="LineEdit" parent="Control/Dialogs/SettingsDialog/ScrollContainer/GridContainer"] +margin_left = 116.0 margin_top = 100.0 +margin_right = 384.0 +margin_bottom = 124.0 +size_flags_horizontal = 3 + +[node name="OverrideHeightLabel" type="Label" parent="Control/Dialogs/SettingsDialog/ScrollContainer/GridContainer"] +margin_top = 133.0 margin_right = 112.0 -margin_bottom = 104.0 +margin_bottom = 147.0 +text = "Fixed Height" + +[node name="OverrideHeight" type="LineEdit" parent="Control/Dialogs/SettingsDialog/ScrollContainer/GridContainer"] +margin_left = 116.0 +margin_top = 128.0 +margin_right = 384.0 +margin_bottom = 152.0 +size_flags_horizontal = 3 + +[node name="HSeparator" type="HSeparator" parent="Control/Dialogs/SettingsDialog/ScrollContainer/GridContainer"] +margin_top = 156.0 +margin_right = 112.0 +margin_bottom = 160.0 __meta__ = { "_edit_use_anchors_": false } -[node name="HSeparator2" type="HSeparator" parent="Control/Dialogs/SettingsDialog/GridContainer"] -visible = false -margin_top = 100.0 -margin_right = 182.0 -margin_bottom = 131.0 +[node name="HSeparator2" type="HSeparator" parent="Control/Dialogs/SettingsDialog/ScrollContainer/GridContainer"] +margin_left = 116.0 +margin_top = 156.0 +margin_right = 384.0 +margin_bottom = 160.0 __meta__ = { "_edit_use_anchors_": false } -[node name="AutoLaunchBurritoLinkLabel" type="Label" parent="Control/Dialogs/SettingsDialog/GridContainer"] -visible = false -margin_top = 104.0 +[node name="AutoLaunchBurritoLinkLabel" type="Label" parent="Control/Dialogs/SettingsDialog/ScrollContainer/GridContainer"] +margin_top = 168.0 margin_right = 112.0 -margin_bottom = 135.0 +margin_bottom = 199.0 text = "Auto Launch Burrito Link" -[node name="AutoLaunchBurritoLink" type="CheckButton" parent="Control/Dialogs/SettingsDialog/GridContainer"] -visible = false -margin_top = 100.0 -margin_right = 182.0 -margin_bottom = 140.0 +[node name="AutoLaunchBurritoLink" type="CheckButton" parent="Control/Dialogs/SettingsDialog/ScrollContainer/GridContainer"] +margin_left = 116.0 +margin_top = 164.0 +margin_right = 384.0 +margin_bottom = 204.0 size_flags_horizontal = 3 -[node name="WinePathLabel" type="Label" parent="Control/Dialogs/SettingsDialog/GridContainer"] -visible = false -margin_top = 105.0 +[node name="WinePathLabel" type="Label" parent="Control/Dialogs/SettingsDialog/ScrollContainer/GridContainer"] +margin_top = 213.0 margin_right = 112.0 -margin_bottom = 119.0 +margin_bottom = 227.0 text = "Wine Path" -[node name="WinePath" type="LineEdit" parent="Control/Dialogs/SettingsDialog/GridContainer"] -visible = false -margin_top = 100.0 -margin_right = 182.0 -margin_bottom = 124.0 +[node name="WinePath" type="LineEdit" parent="Control/Dialogs/SettingsDialog/ScrollContainer/GridContainer"] +margin_left = 116.0 +margin_top = 208.0 +margin_right = 384.0 +margin_bottom = 232.0 size_flags_horizontal = 3 -[node name="EnvironmentVarsLabel" type="Label" parent="Control/Dialogs/SettingsDialog/GridContainer"] -visible = false -margin_top = 143.0 +[node name="EnvironmentVarsLabel" type="Label" parent="Control/Dialogs/SettingsDialog/ScrollContainer/GridContainer"] +margin_top = 279.0 margin_right = 112.0 -margin_bottom = 157.0 +margin_bottom = 293.0 text = "Environment Vars" -[node name="EnvironmentVars" type="TextEdit" parent="Control/Dialogs/SettingsDialog/GridContainer"] -visible = false -margin_top = 100.0 -margin_right = 182.0 -margin_bottom = 200.0 +[node name="EnvironmentVars" type="TextEdit" parent="Control/Dialogs/SettingsDialog/ScrollContainer/GridContainer"] +margin_left = 116.0 +margin_top = 236.0 +margin_right = 384.0 +margin_bottom = 336.0 rect_min_size = Vector2( 0, 100 ) size_flags_horizontal = 3 @@ -742,6 +776,20 @@ __meta__ = { "_edit_use_anchors_": false } +[node name="Spacer" type="Control" parent="Control/Dialogs/SettingsDialog/ScrollContainer/GridContainer"] +visible = false +margin_top = 284.0 +margin_right = 112.0 +margin_bottom = 284.0 + +[node name="LoadLutrisProfile" type="Button" parent="Control/Dialogs/SettingsDialog/ScrollContainer/GridContainer"] +visible = false +margin_left = 116.0 +margin_top = 284.0 +margin_right = 384.0 +margin_bottom = 304.0 +text = "Load Lutris Profile" + [node name="Border" type="Control" parent="Control"] visible = false anchor_right = 1.0 @@ -865,12 +913,14 @@ material/0 = SubResource( 4 ) [connection signal="pressed" from="Control/Dialogs/NodeEditorDialog/ScrollContainer/VBoxContainer/SetActivePath" to="." method="_on_SetActivePath_pressed"] [connection signal="pressed" from="Control/Dialogs/NodeEditorDialog/ScrollContainer/VBoxContainer/ReversePathDirection" to="." method="_on_ReversePathDirection_pressed"] [connection signal="hide" from="Control/Dialogs/SettingsDialog" to="." method="_on_NodeEditorDialog_hide"] -[connection signal="pressed" from="Control/Dialogs/SettingsDialog/GridContainer/OverrideSize" to="Control/Dialogs/SettingsDialog" method="save_settings"] -[connection signal="text_changed" from="Control/Dialogs/SettingsDialog/GridContainer/OverrideWidth" to="Control/Dialogs/SettingsDialog" method="save_settings"] -[connection signal="text_changed" from="Control/Dialogs/SettingsDialog/GridContainer/OverrideHeight" to="Control/Dialogs/SettingsDialog" method="save_settings"] -[connection signal="pressed" from="Control/Dialogs/SettingsDialog/GridContainer/AutoLaunchBurritoLink" to="Control/Dialogs/SettingsDialog" method="save_settings"] -[connection signal="text_changed" from="Control/Dialogs/SettingsDialog/GridContainer/WinePath" to="Control/Dialogs/SettingsDialog" method="save_settings"] -[connection signal="text_changed" from="Control/Dialogs/SettingsDialog/GridContainer/EnvironmentVars" to="Control/Dialogs/SettingsDialog" method="save_settings"] +[connection signal="pressed" from="Control/Dialogs/SettingsDialog/ScrollContainer/GridContainer/OverrideSize" to="Control/Dialogs/SettingsDialog" method="save_settings"] +[connection signal="text_changed" from="Control/Dialogs/SettingsDialog/ScrollContainer/GridContainer/MinimumWidth" to="Control/Dialogs/SettingsDialog" method="save_settings"] +[connection signal="text_changed" from="Control/Dialogs/SettingsDialog/ScrollContainer/GridContainer/MinimumHeight" to="Control/Dialogs/SettingsDialog" method="save_settings"] +[connection signal="text_changed" from="Control/Dialogs/SettingsDialog/ScrollContainer/GridContainer/OverrideWidth" to="Control/Dialogs/SettingsDialog" method="save_settings"] +[connection signal="text_changed" from="Control/Dialogs/SettingsDialog/ScrollContainer/GridContainer/OverrideHeight" to="Control/Dialogs/SettingsDialog" method="save_settings"] +[connection signal="pressed" from="Control/Dialogs/SettingsDialog/ScrollContainer/GridContainer/AutoLaunchBurritoLink" to="Control/Dialogs/SettingsDialog" method="save_settings"] +[connection signal="text_changed" from="Control/Dialogs/SettingsDialog/ScrollContainer/GridContainer/WinePath" to="Control/Dialogs/SettingsDialog" method="save_settings"] +[connection signal="text_changed" from="Control/Dialogs/SettingsDialog/ScrollContainer/GridContainer/EnvironmentVars" to="Control/Dialogs/SettingsDialog" method="save_settings"] [connection signal="hide" from="Control/Dialogs/MarkerPacks" to="." method="_on_Dialog_hide"] [connection signal="cell_selected" from="Control/Dialogs/MarkerPacks/MarkerPacks" to="." method="_on_MarkerPacks_cell_selected"] [connection signal="item_edited" from="Control/Dialogs/MarkerPacks/MarkerPacks" to="." method="_on_MarkerPacks_item_edited"] diff --git a/burrito_link/.clang-format b/burrito_link/.clang-format new file mode 100644 index 00000000..ce7356b5 --- /dev/null +++ b/burrito_link/.clang-format @@ -0,0 +1,149 @@ +--- +Language: Cpp # Validate C++ +# BasedOnStyle: Google +AccessModifierOffset: -3 # With a tab indent of 4 spaces this equals a 1 space access modifier indent. +AlignAfterOpenBracket: AlwaysBreak # Should be `BlockIndent` when it begins to work. +AlignConsecutiveMacros: false # Keep the value of a macro close to the macro name. +AlignConsecutiveAssignments: false # Keep the new value close to the variable it is being assigned to. +AlignConsecutiveDeclarations: false # Keep new variable names close to their type definitions. +AlignEscapedNewlines: DontAlign # Keep `\` for escaped newlines close to the last character in the line. +AlignOperands: true +AlignTrailingComments: false # Keep trailing commens close to the last character in the line. +AllowShortBlocksOnASingleLine: Never # Force short blocks to have proper newlines. +AllowShortCaseLabelsOnASingleLine: false # Force contents of case statements onto the next line. +AllowShortFunctionsOnASingleLine: None +AllowShortLambdasOnASingleLine: All +AllowShortIfStatementsOnASingleLine: WithoutElse +AllowShortLoopsOnASingleLine: true +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: true +AlwaysBreakTemplateDeclarations: Yes +BinPackArguments: false # Dont binpack anything +BinPackParameters: false # Dont binpack anything +BraceWrapping: + AfterCaseLabel: false # Keep the open brace on the same line as the label + AfterClass: false # Keep the open brace on the same line as the class + AfterControlStatement: false + AfterEnum: false # V + AfterFunction: false # V + AfterNamespace: false # V + AfterObjCDeclaration: false + AfterStruct: false # V + AfterUnion: false # V + AfterExternBlock: false # V + BeforeCatch: true # V + BeforeElse: true # V + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakBeforeBraces: Custom +BreakBeforeInheritanceComma: false +BreakInheritanceList: BeforeColon +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: BeforeColon +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: true +ColumnLimit: 0 # Line break auto-formatting is very difficult. Ignoring it for now +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: true +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DeriveLineEnding: true +DerivePointerAlignment: true +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IncludeBlocks: Regroup +IncludeCategories: + - Regex: '^' + Priority: 2 + SortPriority: 0 + - Regex: '^<.*\.h>' + Priority: 1 + SortPriority: 0 + - Regex: '^<.*' + Priority: 2 + SortPriority: 0 + - Regex: '.*' + Priority: 3 + SortPriority: 0 +IncludeIsMainRegex: '([-_](test|unittest))?$' +IncludeIsMainSourceRegex: '' +IndentCaseLabels: true +IndentGotoLabels: true +IndentPPDirectives: None +IndentWidth: 4 # Use 4 spaces for an indent +IndentWrappedFunctionNames: false +KeepEmptyLinesAtTheStartOfBlocks: false +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +PointerAlignment: Left +RawStringFormats: + - Language: Cpp + Delimiters: + - cc + - CC + - cpp + - Cpp + - CPP + - 'c++' + - 'C++' + CanonicalDelimiter: '' + BasedOnStyle: google + - Language: TextProto + Delimiters: + - pb + - PB + - proto + - PROTO + EnclosingFunctions: + - EqualsProto + - EquivToProto + - PARSE_PARTIAL_TEXT_PROTO + - PARSE_TEST_PROTO + - PARSE_TEXT_PROTO + - ParseTextOrDie + - ParseTextProtoOrDie + CanonicalDelimiter: '' + BasedOnStyle: google +ReflowComments: true +SortIncludes: true +SortUsingDeclarations: true +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyBlock: false +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 2 +SpacesInAngles: false +SpacesInConditionalStatement: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +SpaceBeforeSquareBrackets: false +Standard: Auto +StatementMacros: + - Q_UNUSED + - QT_REQUIRE_VERSION +TabWidth: 8 +UseCRLF: false +UseTab: Never +... diff --git a/burrito_link/.gitignore b/burrito_link/.gitignore new file mode 100644 index 00000000..7764270d --- /dev/null +++ b/burrito_link/.gitignore @@ -0,0 +1,3 @@ +.clangd/ +build/ +compile_commands.json diff --git a/burrito_link/CMakeLists.txt b/burrito_link/CMakeLists.txt new file mode 100644 index 00000000..fc25597e --- /dev/null +++ b/burrito_link/CMakeLists.txt @@ -0,0 +1,41 @@ +cmake_minimum_required(VERSION 3.2.0 FATAL_ERROR) + +project (burrito_link) + +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# remove `-rdynamic` default link library flag set in the linux `Platform/*.cmake` file +SET(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS) + +# Set the compiler to mingw so we can build a windows executable on linux +SET(CMAKE_C_COMPILER x86_64-w64-mingw32-gcc) + +# Create the standalone executable +set(STANDALONE burrito_link.exe) +add_executable (${STANDALONE} "burrito_link.c") +target_link_libraries(${STANDALONE} PRIVATE "ws2_32") + +target_compile_options(${STANDALONE} PRIVATE -Wall -Os -g) + +# An option specifically for creating console application and not a windowed application +target_compile_options(${STANDALONE} PRIVATE -mconsole) + + +# Create the D3D11.dll file +set(CMAKE_SYSTEM_NAME Windows) + +SET(DX11LIB d3d11.dll) + +# Will this build a library +add_library(${DX11LIB} SHARED dllmain.c burrito_link.c) + +target_compile_options(${DX11LIB} PRIVATE -mwindows) +target_compile_options(${DX11LIB} PRIVATE -static-libgcc -static-libstdc++) + +target_link_libraries(${DX11LIB} PRIVATE "ws2_32") + +set_target_properties(${DX11LIB} PROPERTIES + PREFIX "" + SUFFIX "" + LINK_FLAGS "../deffile.def -Wl,--allow-shlib-undefined -Wl,-O1 -shared -static -static-libgcc -static-libstdc++ -Wl,--file-alignment=4096 -lm -lkernel32 -luser32 -lgdi32 -lwinspool -lshell32 -lole32 -loleaut32 -luuid -lcomdlg32 -ladvapi32" +) diff --git a/burrito_link/Makefile b/burrito_link/Makefile deleted file mode 100644 index b4091a35..00000000 --- a/burrito_link/Makefile +++ /dev/null @@ -1,21 +0,0 @@ -# all: gw2mumbleudp.exe -all: burrito_link.exe - -CC=gcc -WINECC=i686-w64-mingw32-gcc -CFLAGS=-Wall -Os -g -lws2_32 - -# assets/shmwrapper2.bin: shmwrapper2.c -# $(CC) $< $(CFLAGS) -o $@ - -# gw2mumbleudp.exe: gw2mumbleudp.c -burrito_link.exe: gw2mumbleudp.c - $(WINECC) $< $(CFLAGS) -mconsole -o $@ - -# bindata.go: assets/shmwrapper1.exe assets/shmwrapper2.bin -# go-bindata -pkg=wineshm -ignore=.gitkeep assets/ - -clean: - rm -f assets/shmwrapper1.exe assets/shmwrapper2.bin bindata.go - -# vim: syntax=make ts=4 sw=4 sts=4 sr noet diff --git a/burrito_link/gw2mumbleudp.c b/burrito_link/burrito_link.c similarity index 61% rename from burrito_link/gw2mumbleudp.c rename to burrito_link/burrito_link.c index 1181cfd1..b5f5dc64 100644 --- a/burrito_link/gw2mumbleudp.c +++ b/burrito_link/burrito_link.c @@ -1,31 +1,47 @@ +#include #include +#include +#include +#include #include #include -#include + +#include "linked_memory.h" + +// Enumerations of the different packet types that can be sent +#define PACKET_FRAME 1 +#define PACKET_METADATA 2 +#define PACKET_LINK_TIMEOUT 3 + + +// The max buffer size for data that is being sent to burriot over the UDP socket +#define MaxBufferSize 1024 + +// A state variable to keep track of the previous cycle's map_id to determine +// if the map_id has changed in this cycle. +int last_map_id = 0; + // Do a rolling average on the player position because that seems to be // Roughly what the camera position is doing and doing this will remove // some weird jitter I hope -struct rolling_average_5 -{ +struct rolling_average_5 { UINT8 index; float points[5]; }; -float get_rolling_average(struct rolling_average_5 *points) -{ + +float get_rolling_average(struct rolling_average_5 *points) { float sum = 0; - for (int i = 0; i < 5; i++) - { + for (int i = 0; i < 5; i++) { sum += points->points[i]; } return sum / 5.0; } -void replace_point_in_rolling_average(struct rolling_average_5 *points, float newvalue) -{ + +void replace_point_in_rolling_average(struct rolling_average_5 *points, float newvalue) { points->points[points->index] = newvalue; points->index = points->index + 1; - if (points->index > 4) - { + if (points->index > 4) { points->index = 0; } } @@ -35,95 +51,52 @@ struct rolling_average_5 playerz_avg; float fAvatarAveragePosition[3]; -// https://wiki.guildwars2.com/wiki/API:MumbleLink -struct LinkedMem -{ - UINT32 uiVersion; - DWORD uiTick; - float fAvatarPosition[3]; // The XYZ location of the player - float fAvatarFront[3]; - float fAvatarTop[3]; - wchar_t name[256]; // The string "Guild Wars 2" [Ignored] - float fCameraPosition[3]; // The XYZ position of the camera - float fCameraFront[3]; // A unit vector extending out the front of the camera - float fCameraTop[3]; // A perpendicular vector to fCameraFront, used for calculating roll [Ignored] - wchar_t identity[256]; // A json string containing json data - UINT32 context_len; // A value that is always 48 [Ignored] - unsigned char context[256]; // See MumbleContext struct - wchar_t description[2048]; // Empty [Ignored] -}; - -struct MumbleContext -{ - unsigned char serverAddress[28]; // contains sockaddr_in or sockaddr_in6 // IGNORED - UINT32 mapId; - UINT32 mapType; - UINT32 shardId; - UINT32 instance; - UINT32 buildId; - // Additional data beyond the 48 bytes Mumble uses for identification - UINT32 uiState; // Bitmask: Bit 1 = IsMapOpen, Bit 2 = IsCompassTopRight, Bit 3 = DoesCompassHaveRotationEnabled, Bit 4 = Game has focus, Bit 5 = Is in Competitive game mode, Bit 6 = Textbox has focus, Bit 7 = Is in Combat - UINT16 compassWidth; // pixels - UINT16 compassHeight; // pixels - float compassRotation; // radians - float playerX; // continentCoords - float playerY; // continentCoords - float mapCenterX; // continentCoords - float mapCenterY; // continentCoords - float mapScale; - UINT32 processId; // Windows process id - UINT8 mountIndex; -}; - // global variables // mumble link pointer struct LinkedMem *lm = NULL; + // mumble context pointer into the `lm` variable above. struct MumbleContext *lc = NULL; -#ifdef _WIN32 + +long program_timeout = 0; +long program_startime = 0; + // handle to the shared memory of Mumble link . close at the end of program. windows will only release the shared memory once ALL handles are closed, // so we don't have to worry about other processes like arcdps or other overlays if they are using this. HANDLE handle_lm; // the pointer to the mapped view of the file. close before handle. LPCTSTR mapped_lm; -#endif - -void initMumble() -{ -#ifdef _WIN32 +void initMumble() { // creates a shared memory IF it doesn't exist. otherwise, it returns the existing shared memory handle. // reference: https://docs.microsoft.com/en-us/windows/win32/memory/creating-named-shared-memory size_t BUF_SIZE = sizeof(struct LinkedMem); handle_lm = CreateFileMapping( - INVALID_HANDLE_VALUE, // use paging file - NULL, // default security - PAGE_READWRITE, // read/write access - 0, // maximum object size (high-order DWORD) - BUF_SIZE, // maximum object size (low-order DWORD) - "MumbleLink"); // name of mapping object - // createfilemapping returns NULL when it fails, we print the error code for debugging purposes. - - if (handle_lm == NULL) - { - printf("Could not create file mapping object (%lu).\n", - GetLastError()); + INVALID_HANDLE_VALUE, // use paging file + NULL, // default security + PAGE_READWRITE, // read/write access + 0, // maximum object size (high-order DWORD) + BUF_SIZE, // maximum object size (low-order DWORD) + "MumbleLink"); // name of mapping object + + // CreateFileMapping returns NULL when it fails, we print the error code for debugging purposes. + if (handle_lm == NULL) { + printf("Could not create file mapping object (%lu).\n", GetLastError()); return; } - mapped_lm = (LPTSTR)MapViewOfFile(handle_lm, // handle to map object - FILE_MAP_ALL_ACCESS, // read/write permission - 0, - 0, - BUF_SIZE); + mapped_lm = (LPTSTR)MapViewOfFile( + handle_lm, // handle to map object + FILE_MAP_ALL_ACCESS, // read/write permission + 0, + 0, + BUF_SIZE); - if (mapped_lm == NULL) - { - printf("Could not map view of file (%lu).\n", - GetLastError()); + if (mapped_lm == NULL) { + printf("Could not map view of file (%lu).\n", GetLastError()); CloseHandle(handle_lm); @@ -133,44 +106,56 @@ void initMumble() lc = (struct MumbleContext *)lm->context; printf("successfully opened mumble link shared memory..\n"); -#else - char memname[256]; - snprintf(memname, 256, "/MumbleLink.%d", getuid()); +} - int shmfd = shm_open(memname, O_RDWR, S_IRUSR | S_IWUSR); - if (shmfd < 0) - { - return; +//////////////////////////////////////////////////////////////////////////////// +// x11_window_id_from_windows_process_id() +// +// When running a program in wine a property `__wine_x11_whole_window` is set. +// This function attempts to read that property and return it. +//////////////////////////////////////////////////////////////////////////////// +uint32_t x11_window_id_from_windows_process_id(uint32_t windows_process_id) { + // Get and send the linux x server window id + UINT32 x11_window_id = 0; + HWND window_handle = NULL; + BOOL CALLBACK EnumWindowsProcMy(HWND hwnd, LPARAM lParam) { + DWORD processId; + GetWindowThreadProcessId(hwnd, &processId); + if (processId == lParam) { + window_handle = hwnd; + return FALSE; + } + return TRUE; } + EnumWindows(EnumWindowsProcMy, windows_process_id); - lm = (struct LinkedMem *)(mmap(NULL, sizeof(struct LinkedMem), PROT_READ | PROT_WRITE, MAP_SHARED, shmfd, 0)); - - if (lm == (void *)(-1)) - { - lm = NULL; - return; + HANDLE possible_x11_window_id = GetProp(window_handle, "__wine_x11_whole_window"); + if (possible_x11_window_id != NULL) { + x11_window_id = (size_t)possible_x11_window_id; } -#endif + // else { + // printf("No Linux ID\n"); + // } + return x11_window_id; } -int last_map_id = 0; - -#define MaxBufferSize 1024 -int connect_and_or_send() -{ +//////////////////////////////////////////////////////////////////////////////// +// connect_and_or_send() +// +// This function loops until termination, grabbing information from the shared +// memory block and sending the memory over to burrito over a UDP socket. +//////////////////////////////////////////////////////////////////////////////// +int connect_and_or_send() { WSADATA wsaData; SOCKET SendingSocket; - SOCKADDR_IN ReceiverAddr, SrcInfo; + SOCKADDR_IN ReceiverAddr; int Port = 4242; int BufLength = 1024; char SendBuf[MaxBufferSize]; - int len; int TotalByteSent; - if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) - { - + if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { printf("Client: WSAStartup failed with error %d\n", WSAGetLastError()); // Clean up @@ -179,17 +164,14 @@ int connect_and_or_send() // Exit with error return -1; } - else - { + else { printf("Client: The Winsock DLL status is %s.\n", wsaData.szSystemStatus); } // Create a new socket to receive datagrams on. SendingSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); - if (SendingSocket == INVALID_SOCKET) - { - + if (SendingSocket == INVALID_SOCKET) { // Print error message printf("Client: Error at socket(): %d\n", WSAGetLastError()); @@ -199,8 +181,7 @@ int connect_and_or_send() // Exit with error return -1; } - else - { + else { printf("Client: socket() is OK!\n"); } @@ -218,16 +199,24 @@ int connect_and_or_send() int count = 0; DWORD lastuitick = 0; // Send data packages to the receiver(Server). - do - { + do { + if (program_timeout != 0 && clock() - program_startime > program_timeout) { + BufLength = 1; + // Set the first byte of the packet to indicate this packet is a `Heaver Context Updater` packet + SendBuf[0] = PACKET_LINK_TIMEOUT; + TotalByteSent = sendto(SendingSocket, SendBuf, BufLength, 0, (SOCKADDR *)&ReceiverAddr, sizeof(ReceiverAddr)); + if (TotalByteSent != BufLength) { + printf("Not all Bytes Sent"); + } - if (lm->uiTick == lastuitick) - { + printf("Breaking out due to timeout"); + break; + } + if (lm->uiTick == lastuitick) { Sleep(1); continue; } lastuitick = lm->uiTick; - //printf("%ld\n", lm->uiTick); replace_point_in_rolling_average(&playerx_avg, lm->fAvatarPosition[0]); replace_point_in_rolling_average(&playery_avg, lm->fAvatarPosition[1]); @@ -239,7 +228,8 @@ int connect_and_or_send() fAvatarAveragePosition[2] = get_rolling_average(&playerz_avg); BufLength = 1; - SendBuf[0] = 1; // Per Frame Updater + // Set the first byte of the packet to indicate this packet is a `Per Frame Updater` packet + SendBuf[0] = PACKET_FRAME; memcpy(SendBuf + BufLength, lm->fCameraPosition, sizeof(lm->fCameraPosition)); BufLength += sizeof(lm->fCameraPosition); @@ -279,12 +269,18 @@ int connect_and_or_send() // printf("UI State: %i\n", lc->uiState); // Bitmask: Bit 1 = IsMapOpen, Bit 2 = IsCompassTopRight, Bit 3 = DoesCompassHaveRotationEnabled, Bit 4 = Game has focus, Bit 5 = Is in Competitive game mode, Bit 6 = Textbox has focus, Bit 7 = Is in Combat TotalByteSent = sendto(SendingSocket, SendBuf, BufLength, 0, (SOCKADDR *)&ReceiverAddr, sizeof(ReceiverAddr)); + if (TotalByteSent != BufLength) { + printf("Not all Bytes Sent"); + } - if (count == 0 || lc->mapId != last_map_id) - { + // After so many iterations have passed or under specific conditions + // we will send a larger packet that contains more information about + // the current state of the game. + if (count == 0 || lc->mapId != last_map_id) { last_map_id = lc->mapId; BufLength = 1; - SendBuf[0] = 2; // Heaver Context Updater + // Set the first byte of the packet to indicate this packet is a `Heaver Context Updater` packet + SendBuf[0] = PACKET_METADATA; // printf("hello world\n"); // printf("%ls\n", lm->description); @@ -327,35 +323,11 @@ int connect_and_or_send() memcpy(SendBuf + BufLength, &lc->mapId, sizeof(lc->mapId)); BufLength += sizeof(lc->mapId); - - // Get and send the linux x server window id - UINT32 x11_window_id = 0; - HWND window_handle=NULL; - BOOL CALLBACK EnumWindowsProcMy(HWND hwnd, LPARAM lParam) - { - DWORD processId; - GetWindowThreadProcessId(hwnd, &processId); - if(processId == lParam) - { - window_handle=hwnd; - return FALSE; - } - return TRUE; - } - EnumWindows(EnumWindowsProcMy, lc->processId); - - HANDLE possible_x11_window_id = GetProp(window_handle, "__wine_x11_whole_window"); - if (possible_x11_window_id != NULL) { - x11_window_id = (size_t)possible_x11_window_id; - } - // else { - // printf("No Linux ID\n"); - // } + uint32_t x11_window_id = x11_window_id_from_windows_process_id(lc->processId); memcpy(SendBuf + BufLength, &x11_window_id, sizeof(x11_window_id)); BufLength += sizeof(x11_window_id); - // Convert and send the JSON 'identity' payload char utf8str[1024]; @@ -380,56 +352,24 @@ int connect_and_or_send() BufLength += converted_size; TotalByteSent = sendto(SendingSocket, SendBuf, BufLength, 0, (SOCKADDR *)&ReceiverAddr, sizeof(ReceiverAddr)); - + if (TotalByteSent != BufLength) { + printf("Not all Bytes Sent"); + } // break; } - // Sleep(16); // Slightly faster then 60fps which would be 16.6666666...ms - + // Update the count for the `Heaver Context Updater` packet and reset + // it to 0 when it hits a threshold value. count += 1; - if (count > 500) - { + if (count > 500) { count = 0; } - - // TODO: Maybe make a way to break out of this loop beyond program termination } while (TRUE); - // Print some info on the receiver(Server) side... - - // Allocate the required resources - - memset(&SrcInfo, 0, sizeof(SrcInfo)); - - len = sizeof(SrcInfo); - - getsockname(SendingSocket, (SOCKADDR *)&SrcInfo, &len); - - printf("Client: Sending IP(s) used: %s\n", inet_ntoa(SrcInfo.sin_addr)); - - printf("Client: Sending port used: %d\n", htons(SrcInfo.sin_port)); - - // Print some info on the sender(Client) side... - - getpeername(SendingSocket, (SOCKADDR *)&ReceiverAddr, (int *)sizeof(ReceiverAddr)); - - printf("Client: Receiving IP used: %s\n", inet_ntoa(ReceiverAddr.sin_addr)); - - printf("Client: Receiving port used: %d\n", htons(ReceiverAddr.sin_port)); - - printf("Client: Total byte sent: %d\n", TotalByteSent); - - // When your application is finished receiving datagrams close the socket. - - printf("Client: Finished sending. Closing the sending socket...\n"); - - if (closesocket(SendingSocket) != 0) - { - + if (closesocket(SendingSocket) != 0) { printf("Client: closesocket() failed! Error code: %d\n", WSAGetLastError()); } - else - { + else { printf("Server: closesocket() is OK\n"); } @@ -437,40 +377,45 @@ int connect_and_or_send() printf("Client: Cleaning up...\n"); - if (WSACleanup() != 0) - { + if (WSACleanup() != 0) { printf("Client: WSACleanup() failed! Error code: %d\n", WSAGetLastError()); } - - else - { + else { printf("Client: WSACleanup() is OK\n"); } -#ifdef _WIN32 + // unmap the shared memory from our process address space. UnmapViewOfFile(mapped_lm); // close LinkedMemory handle CloseHandle(handle_lm); -#endif // Back to the system return 0; } -int main(int argc, char **argv) -{ +void run_link() { playerx_avg.index = 0; playery_avg.index = 0; playerz_avg.index = 0; - printf("hello world\n"); initMumble(); - // sockmain(argc, argv); - // initMumble(); - // for (int i = 0; i < 100; i++) { - // printf("%f\n", lm->fAvatarPosition[0]); - // Sleep(16); // Slightly faster then 60fps which would be 16.6666666...ms - // } connect_and_or_send(); -} \ No newline at end of file +} + +//////////////////////////////////////////////////////////////////////////////// +// The main function initializes some global variables and shared memory. Then +// calls the connect_and_or_send process which loops until termination. +//////////////////////////////////////////////////////////////////////////////// +int main(int argc, char **argv) { + for (int i = 0; i < argc; i++) { + // If a timeout flag is passed in then set the timeout value + if (strcmp(argv[i], "--timeout") == 0) { + i = i + 1; + program_timeout = atol(argv[i]) * CLOCKS_PER_SEC; + program_startime = clock(); + } + } + + run_link(); +} diff --git a/burrito_link/deffile.def b/burrito_link/deffile.def new file mode 100644 index 00000000..eb57a4e1 --- /dev/null +++ b/burrito_link/deffile.def @@ -0,0 +1,7 @@ +LIBRARY d3d11.dll +EXPORTS + D3D11CreateDevice + D3D11CreateDeviceAndSwapChain + D3D11CoreCreateDevice + get_init_addr + get_release_addr diff --git a/burrito_link/dllmain.c b/burrito_link/dllmain.c new file mode 100644 index 00000000..62ea9ef3 --- /dev/null +++ b/burrito_link/dllmain.c @@ -0,0 +1,405 @@ +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#include + +#ifndef true +#define true TRUE +#endif + +#ifndef false +#define false FALSE +#endif + +#ifndef nullptr +#define nullptr 0 +#endif + +#define CEXTERN extern + + +//////////////////////////////////////////////////////////////////////////////// +// GetOriginalD3D11Module +// +// This loads the original d3d11.dll file so that we can pass the function +// calls made to this library into that library as if they were implemented in +// this library. +//////////////////////////////////////////////////////////////////////////////// +HMODULE D3D11Library = nullptr; +HMODULE GetOriginalD3D11Module() { + // If we have already loaded the library into this process return the + // pointer to it instead of loading it a second time. + if (!D3D11Library) { + char infoBuf[4096]; + GetSystemDirectory(infoBuf, 4096); + lstrcat(infoBuf, "\\d3d11.dll"); + + // Load this library and track it. We don't want to use GetModuleHandle + // here because GetModuleHandle does not increment the internal + // reference count meaning that FreeLibrary would incorrectly decrement + // it when we unload. + D3D11Library = LoadLibrary(infoBuf); + } + + return D3D11Library; +} + + +//////////////////////////////////////////////////////////////////////////////// +// FreeD3D11Module +// +// This frees the original d3d11.dll file so that this process no longer is +// using it. +//////////////////////////////////////////////////////////////////////////////// +void FreeD3D11Module() { + if (D3D11Library) { + FreeLibrary(D3D11Library); + + } +} + + + +//////////////////////////////////////////////////////////////////////////////// +// D3D11CreateDeviceAndSwapChainOriginal +// +// A pointer to a function that looks like D3D11CreateDeviceAndSwapChain() +//////////////////////////////////////////////////////////////////////////////// +typedef HRESULT(WINAPI* D3D11CreateDeviceAndSwapChainFunc)( + IDXGIAdapter* pAdapter, + D3D_DRIVER_TYPE DriverType, + HMODULE Software, + UINT Flags, + const D3D_FEATURE_LEVEL* pFeatureLevels, + UINT FeatureLevels, + UINT SDKVersion, + const DXGI_SWAP_CHAIN_DESC* pSwapChainDesc, + IDXGISwapChain** ppSwapChain, + ID3D11Device** ppDevice, + D3D_FEATURE_LEVEL* pFeatureLevel, + ID3D11DeviceContext** ppImmediateContext +); +D3D11CreateDeviceAndSwapChainFunc D3D11CreateDeviceAndSwapChainOriginal = nullptr; + +//////////////////////////////////////////////////////////////////////////////// +// D3D11CreateDeviceAndSwapChain +// +// A proxy function that calls the original d3d11.dll's +// D3D11CreateDeviceAndSwapChain function, then returns the result. +//////////////////////////////////////////////////////////////////////////////// +CEXTERN HRESULT WINAPI D3D11CreateDeviceAndSwapChain( + IDXGIAdapter* pAdapter, + D3D_DRIVER_TYPE DriverType, + HMODULE Software, + UINT Flags, + const D3D_FEATURE_LEVEL* pFeatureLevels, + UINT FeatureLevels, + UINT SDKVersion, + const DXGI_SWAP_CHAIN_DESC* pSwapChainDesc, + IDXGISwapChain** ppSwapChain, + ID3D11Device** ppDevice, + D3D_FEATURE_LEVEL* pFeatureLevel, + ID3D11DeviceContext** ppImmediateContext +) { + printf("FUNCTION: D3D11CreateDeviceAndSwapChain\n"); + fflush(stdout); + // Get the function address if we don't have it yet. + if (!D3D11CreateDeviceAndSwapChainOriginal) { + HMODULE original_d3d11 = GetOriginalD3D11Module(); + D3D11CreateDeviceAndSwapChainOriginal = (D3D11CreateDeviceAndSwapChainFunc)GetProcAddress(original_d3d11, "D3D11CreateDeviceAndSwapChain"); + } + + return D3D11CreateDeviceAndSwapChainOriginal( + pAdapter, + DriverType, + Software, + Flags, + pFeatureLevels, + FeatureLevels, + SDKVersion, + pSwapChainDesc, + ppSwapChain, + ppDevice, + pFeatureLevel, + ppImmediateContext + ); +} + + +//////////////////////////////////////////////////////////////////////////////// +// D3D11CreateDeviceOriginal +// +// A pointer to a function that looks like D3D11CreateDevice() +//////////////////////////////////////////////////////////////////////////////// +typedef HRESULT(WINAPI* D3D11CreateDeviceFunc)( + IDXGIAdapter* pAdapter, + D3D_DRIVER_TYPE DriverType, + HMODULE Software, + UINT Flags, + const D3D_FEATURE_LEVEL* pFeatureLevels, + UINT FeatureLevels, + UINT SDKVersion, + ID3D11Device** ppDevice, + D3D_FEATURE_LEVEL* pFeatureLevel, + ID3D11DeviceContext** ppImmediateContext +); +D3D11CreateDeviceFunc D3D11CreateDeviceOriginal = nullptr; + +//////////////////////////////////////////////////////////////////////////////// +// D3D11CreateDevice +// +// A proxy function that call the original d3d11.dll's D3D11CreateDevice +// function, then returns the result. +//////////////////////////////////////////////////////////////////////////////// +CEXTERN HRESULT WINAPI D3D11CreateDevice( + IDXGIAdapter* pAdapter, + D3D_DRIVER_TYPE DriverType, + HMODULE Software, + UINT Flags, + const D3D_FEATURE_LEVEL* pFeatureLevels, + UINT FeatureLevels, + UINT SDKVersion, + ID3D11Device** ppDevice, + D3D_FEATURE_LEVEL* pFeatureLevel, + ID3D11DeviceContext** ppImmediateContext +) { + printf("FUNCTION: D3D11CreateDevice\n"); + fflush(stdout); + // Get the function address if we don't have it yet. + if (!D3D11CreateDeviceOriginal) { + HMODULE original_d3d11 = GetOriginalD3D11Module(); + D3D11CreateDeviceOriginal = (D3D11CreateDeviceFunc)GetProcAddress(original_d3d11, "D3D11CreateDevice"); + } + + return D3D11CreateDeviceOriginal( + pAdapter, + DriverType, + Software, + Flags, + pFeatureLevels, + FeatureLevels, + SDKVersion, + ppDevice, + pFeatureLevel, + ppImmediateContext + ); +} + + +//////////////////////////////////////////////////////////////////////////////// +// D3D11CoreCreateDeviceOriginal +// +// A pointer to a function that looks like D3D11CoreCreateDevice() +//////////////////////////////////////////////////////////////////////////////// +typedef HRESULT(WINAPI* D3D11CoreCreateDeviceFunc)( + IDXGIFactory * pFactory, + IDXGIAdapter * pAdapter, + UINT Flags, + const D3D_FEATURE_LEVEL* pFeatureLevels, + UINT FeatureLevels, + ID3D11Device** ppDevice +); +D3D11CoreCreateDeviceFunc D3D11CoreCreateDeviceOriginal = nullptr; + +//////////////////////////////////////////////////////////////////////////////// +// D3D11CoreCreateDevice +// +// A proxy function that call the original d3d11.dll's D3D11CoreCreateDevice +// function, then returns the result. +//////////////////////////////////////////////////////////////////////////////// +CEXTERN HRESULT WINAPI D3D11CoreCreateDevice( + IDXGIFactory * pFactory, + IDXGIAdapter * pAdapter, + UINT Flags, + const D3D_FEATURE_LEVEL* pFeatureLevels, + UINT FeatureLevels, + ID3D11Device** ppDevice +) { + printf("FUNCTION: D3D11CoreCreateDevice\n"); + fflush(stdout); + // Get the function address if we don't have it yet. + if (!D3D11CoreCreateDeviceOriginal) { + HMODULE original_d3d11 = GetOriginalD3D11Module(); + D3D11CoreCreateDeviceOriginal = (D3D11CoreCreateDeviceFunc)GetProcAddress(original_d3d11, "D3D11CoreCreateDevice"); + } + + return D3D11CoreCreateDeviceOriginal( + pFactory, + pAdapter, + Flags, + pFeatureLevels, + FeatureLevels, + ppDevice + ); +} + + +// Forward declare the run_link() function defined in burrito_link.c +void run_link(); + +// Call the burrito link function from the thread. +// TODO: There is something odd here that causes a crash as gw2 is exiting. +// Because gw2 is exiting the crash does not really matter. I am guessing it +// has something to do with how we handle the infinite wait loop inside +// burrito_link and that we dont clean it up ever. +void WINAPI BurritoLinkThread() { + run_link(); + return; +} + + +//////////////////////////////////////////////////////////////////////////////// +// start_burrito_link_thread +// +// Creates a new burrito link thread if there is no existing burrito link +// thread running. +//////////////////////////////////////////////////////////////////////////////// +HANDLE burrito_link_thread_handle = nullptr; +void start_burrito_link_thread() { + if (burrito_link_thread_handle != nullptr) { + return; + } + + burrito_link_thread_handle = CreateThread( + NULL, + 0, + (LPTHREAD_START_ROUTINE)BurritoLinkThread, + NULL, + 0, + NULL + ); + + if (burrito_link_thread_handle == nullptr) { + // Failed to create the thread. + printf("Failed to create burrito link thread"); + } + +} + + +//////////////////////////////////////////////////////////////////////////////// +// stop_burrito_link_thread +// +// Kills a burrito link thread if there is a burrito link thread running. This +// is nessasry to avoid an error message on exit. +//////////////////////////////////////////////////////////////////////////////// +void stop_burrito_link_thread() { + if (burrito_link_thread_handle != nullptr) { + TerminateThread(burrito_link_thread_handle, 0); + } +} + + +//////////////////////////////////////////////////////////////////////////////// +// DllMain +// +// DllMain is the entry point called when this dll is loaded. We use this +// function to spawn the background threads for burrito_link. +// +// The windows documentation says not to create threads inside of DllMain +// because of two reasons: +// 1) If you wait on the thread you will cause a deadlock. +// 2) Anybody who loads a DLL files would not expect purely loading the file +// to do business logic. +// The first reason is relevant to us, we must make sure to not ever wait on +// the thread or else we will cause a deadlock. The second reason is irrelevant +// as this behavior is the exact unexpected behavior we are trying to take +// advantage of in order to inject our code into the running process. Thus it +// is safe and a good idea to call CreateThread in DllMain. +//////////////////////////////////////////////////////////////////////////////// +BOOL WINAPI DllMain( + HINSTANCE hinstDLL, // handle to the DLL module + DWORD fdwReason, // reason for calling DllMain + LPVOID lpvReserved // Reserved +) { + // printf("FUNCTION: 2 C DllMain "); + // Perform actions based on the reason for calling. + switch(fdwReason) { + // Do process initialization. Return false if initialization fails. + case DLL_PROCESS_ATTACH: + // TODO: Here is where we want to create a new process. + printf("DLL_PROCESS_ATTACH\n"); + start_burrito_link_thread(); + break; + + // Do thread-specific initialization. + case DLL_THREAD_ATTACH: + // printf("DLL_THREAD_ATTACH\n"); + break; + + // Do thread-specific cleanup. + case DLL_THREAD_DETACH: + // printf("DLL_THREAD_DETACH\n"); + break; + + // Do process cleanup + case DLL_PROCESS_DETACH: + // Skip cleanup if process termination scenario + if (lpvReserved != nullptr) { + printf("DLL_PROCESS_DETACH no cleanup\n"); + break; + } + printf("DLL_PROCESS_DETACH\n"); + stop_burrito_link_thread(); + + // Cleanup process + FreeD3D11Module(); + break; + } + fflush(stdout); + + return true; +} + + +//////////////////////////////////////////////////////////////////////////////// +// arcdps +// +// get_init_addr +// get_release_addr +// +// These functions are present to allow arcdps to recognize this dll as a +// plugin for arcdps and run it alongside arcdps. These two functions are the +// only functions that are required for arcdps' api and all others are optional. +//////////////////////////////////////////////////////////////////////////////// +#ifndef ImGuiContext +#define ImGuiContext void +#endif +typedef struct arcdps_exports { + uintptr_t size; + uint32_t sig; + uint32_t imguivers; + const char* out_name; + const char* out_build; + void* wnd_nofilter; + void* combat; + void* imgui; + void* options_end; + void* combat_local; + void* wnd_filter; + void* options_windows; +} arcdps_exports; + +arcdps_exports arc_exports; +arcdps_exports* mod_init() { + memset(&arc_exports, 0, sizeof(arcdps_exports)); + arc_exports.sig = 0xFFFA; + arc_exports.size = sizeof(arcdps_exports); + arc_exports.out_name = "BurritoLink"; + arc_exports.out_build = "1.0"; + return &arc_exports; +} + +extern __declspec(dllexport) void* get_init_addr(char* arcversion, ImGuiContext* imguictx, void* id3dptr, HANDLE arcdll, void* mallocfn, void* freefn, uint32_t d3dversion) { + return mod_init; +} + +uintptr_t mod_release() { + FreeConsole(); + return 0; +} + +extern __declspec(dllexport) void* get_release_addr() { + return mod_release; +} diff --git a/burrito_link/linked_memory.h b/burrito_link/linked_memory.h new file mode 100644 index 00000000..eb2b88b8 --- /dev/null +++ b/burrito_link/linked_memory.h @@ -0,0 +1,132 @@ +#include +#include + +#ifndef wchar_t +#define wchar_t uint16_t +#endif + +//////////////////////////////////////////////////////////////////////////////// +// LinkedMem struct +// +// This struct represents the Mumble Link shared memory datum that Guild Wars 2 +// uses to communicate live player and camera data, as well as some other +// bits of information that are useful for tools like burrito. +// +// https://wiki.guildwars2.com/wiki/API:MumbleLink +// https://www.mumble.info/documentation/developer/positional-audio/link-plugin/ +//////////////////////////////////////////////////////////////////////////////// +struct LinkedMem { + uint32_t uiVersion; + + // The current update tick + uint32_t uiTick; + + // The XYZ location of the player + float fAvatarPosition[3]; + + // A 3D unit vector representing the forward direction of the player character + float fAvatarFront[3]; + + // A 3D unit vector representing the up direction of the player character + float fAvatarTop[3]; + + // The string "Guild Wars 2" + wchar_t name[256]; + + // The XYZ Position of the camera + float fCameraPosition[3]; + + // A 3D unit vector representing the forward direction of the camera + float fCameraFront[3]; + + // A 3D unit vector representing the up direction of the camera + float fCameraTop[3]; + + // A json string containing json data. See https://wiki.guildwars2.com/wiki/API:MumbleLink#identity + wchar_t identity[256]; + + // A value that is always 48 + uint32_t context_len; + + // A binary chunk containing another struct. See the MumbleContext struct below + uint8_t context[256]; + + // An Empty Array, this field is not used by guild wars 2 + wchar_t description[2048]; +}; + + +//////////////////////////////////////////////////////////////////////////////// +// MumbleContext struct +// +// This struct represents the LinkedMem.context datum that is passed in +// LinkedMem. It is a struct that is entirely specific to Guild Wars 2 which +// is why it is seperated out from the more mumble-generic LinkedMem struct. +// +// https://wiki.guildwars2.com/wiki/API:MumbleLink#context +//////////////////////////////////////////////////////////////////////////////// +struct MumbleContext { + // The current address of the guild wars 2 server the player is connected to + // can be a ipv4 `sockaddr_in` or a ipv6 `sockaddr_in6` + uint8_t serverAddress[28]; + + // The Guild Wars 2 id for the map the player is currently in + uint32_t mapId; + + uint32_t mapType; + uint32_t shardId; + uint32_t instance; + uint32_t buildId; + + // A bitmask of various boolean element of the UI state + // Bit 1 = IsMapOpen + // Bit 2 = IsCompassTopRight + // Bit 3 = DoesCompassHaveRotationEnabled + // Bit 4 = Game has focus + // Bit 5 = Is in Competitive game mode + // Bit 6 = Textbox has focus + // Bit 7 = Is in Combat + uint32_t uiState; + + // The width of the minimap in pixels + uint16_t compassWidth; + + // The height of the minimap in pixels + uint16_t compassHeight; + + // The rotation of the minimap contents in radians + float compassRotation; + + // The X location of the player in continentCoords + float playerX; + // The Y location of the player in continentCoords + float playerY; + + // The center X of the current map in continentCoords + float mapCenterX; + // The center Y of the current map in continentCoords + float mapCenterY; + + // The scale of how zoomed in the visible map or minimap is + float mapScale; + + // The windows process id of the Guild Wars 2 process + uint32_t processId; + + // An enum representing which mount is currenty being used by the player + uint8_t mountIndex; + + // Extra bytes that are not currently accounted for or are unused in the context + uint8_t _padding[171]; +}; + +// Sanity check our memory map structs on compile. +static_assert(sizeof(struct LinkedMem) == 5460, "LinkedMem is expected to be 5460 bytes long."); +static_assert(sizeof(struct MumbleContext) == 256, "MumbleContext is expected to be 256 bytes long."); + +// Sanity check our various datatypes on compile. +static_assert(sizeof(float) == 4, "float is expected to be 32 bits long."); +static_assert(sizeof(uint8_t) == 1, "uint8_t is expected to be 8 bits long."); +static_assert(sizeof(uint16_t) == 2, "uint16_t is expected to be 16 bits long."); +static_assert(sizeof(uint32_t) == 4, "uint32_t is expected to be 32 bits long."); +static_assert(sizeof(wchar_t) == 2, "wchar_t is expected to be 16 bits long."); diff --git a/project.godot b/project.godot index c8c06b63..200ad0a0 100644 --- a/project.godot +++ b/project.godot @@ -14,7 +14,7 @@ _global_script_classes=[ { "language": "NativeScript", "path": "res://tacoparser.gdns" }, { -"base": "", +"base": "Node", "class": "X11_FG", "language": "NativeScript", "path": "res://Spatial.gdns"