Skip to content

Instantly share code, notes, and snippets.

@TrueBrain
Last active April 26, 2023 09:20
Show Gist options
  • Save TrueBrain/1a87754b5d5cc8dedceabda826acf923 to your computer and use it in GitHub Desktop.
Save TrueBrain/1a87754b5d5cc8dedceabda826acf923 to your computer and use it in GitHub Desktop.
OpenTTD Opt-In Automated Survey

OpenTTD Opt-In Automated Survey

!! THIS DOCUMENT IS IN PROPOSAL STATE !!

There are many ways to play OpenTTD. There are tons of settings, you can load extensions, from AIs to NewGRFs. They all modify how the game plays.

From a developer perspective, we don't actually know what settings are heavily used, and which are never used. As such, we often find ourself in a discussion about whether something is actually useful. The people we talk most about, are not our average user; as it goes, the most vocal people are not the commoner, but more likely the niche. And although we like to promote as many playstyles as possible, it is easy to focus on those vocal people, instead of the 90%.

From a AI / NewGRF perspective, we don't actually know how many people play a game with my extension. And as such it is difficult to judge what is actually "popular", and what extensions are never really used. This means it is tricky on BaNaNaS to suggest any content.

For both there is a simple solution we have been putting off for a long time now: automated surveys. (one could also call it telemetry, but survey sounds a bit more like what our intent is).

First of all, of course survey will always be opt-in. But to gain a better understanding how OpenTTD is being used, we are considering adding survey.

There are a few groundrules:

  • No personal data or data to track a specific user with.
  • The user has to be able to validate the information that is being sent.
  • Minimize the moments we send survey results.

Possible implementation

To suggest an implementation, we could do the following:

When ever the user closes a game (either Quit the game, Exit the map, Loads another map, etc), we send a JSON blob of data to the survey server. This blob contains:

  • The _settings_game and _settings_client in JSON format, with the exception of all std::string in NetworkSetting. We know there is no personal information in the rest, as they are all bools and integers. Those strings in NetworkSetting will be moved to a new _settings_private, to ensure we don't make accidental mistakes. See here for the specifics.
  • Selected fields in _grfconfig: ident, md5sum, param, palette. Basically, everything that is also in a Savegame, with the exception of filename. This for the simple reason it could leak private information. We might also want to add name, to suplement the information in case the GRF is not on BaNaNaS. See here for the specifics.
  • An unique ID of the Savegame. This ID is generated when a Savegame is created, and is meant to deduplicate the Savegame on the survey server.
  • The playtime of this session. This is meant to give weight to how often things are used. After all, a specific setting that is only used in 10 game-minutes is worth less than a setting used for 10 game-years.
  • The OpenTTD version, including if it is a patchpack or not.
  • OS of the machine.
  • Amount of logical cores available on the machine.
  • The video-driver / music-driver / sound-driver loaded.

The survey server will append the current date to this, and that is it. No other information is tracked.

Opt-in

When the user first starts the game, they will be asked if they want to participate in this survey, and with a "More info" button we explain on a website how we deal with survey, and what is being sent. There should also be a button: preview payload, which shows the JSON payload that would be sent. The user can opt-in here to participate.

Survey server

The survey server stores every JSON blob in a NoSQL database. It is important to note that no IP or anything is logged inside this database. This to ensure we cannot trace back what user played what game in any way, shape or form.

Note: we do rate-limit on IP, but this is done by another system, and only remembers the IP for the period required to enforce the rate-limit.

To prevent just anyone sending payloads to our survey server, our official binaries, and every patchpack that requests it, will receive a public key, with which the payload is signed. Although of course it is trivial to find this public key in the binary if someone would really want to, it does increases the knowledge required to send payloads to our survey server, and prevent development builds to participate.

We will create a new site, survey.openttd.org or something, where these blobs are sent to. Additionally, it will contain a website that shows the results of the survey in a way that is available to everyone. This includes which NewGRFs are used most, and how settings are distributed. For performance reasons, this will not be live, but will update on regular intervals.

@TrueBrain
Copy link
Author

TrueBrain commented Apr 25, 2023

Example snippet of survey JSON:

{
    "game": {
        "companies": {
            "0": {
                "infrastructure": {
                    "airport": 0,
                    "rail": 0,
                    "road": 0,
                    "signal": 0,
                    "station": 0,
                    "tram": 0,
                    "water": 0
                },
                "type": "human",
                "vehicles": {
                    "aircraft": 0,
                    "roadveh": 0,
                    "ship": 0,
                    "train": 0
                }
            },
            "1": {
                "infrastructure": {
                    "airport": 0,
                    "rail": 0,
                    "road": 173,
                    "signal": 0,
                    "station": 4,
                    "tram": 0,
                    "water": 0
                },
                "name": "AroAI",
                "type": "ai",
                "vehicles": {
                    "aircraft": 0,
                    "roadveh": 20,
                    "ship": 0,
                    "train": 0
                },
                "version": 127
            },
            "2": {
                "infrastructure": {
                    "airport": 0,
                    "rail": 268,
                    "road": 0,
                    "signal": 27,
                    "station": 32,
                    "tram": 0,
                    "water": 0
                },
                "name": "AdmiralAI",
                "type": "ai",
                "vehicles": {
                    "aircraft": 0,
                    "roadveh": 0,
                    "ship": 0,
                    "train": 1
                },
                "version": 25
            },
            "3": {
                "infrastructure": {
                    "airport": 0,
                    "rail": 0,
                    "road": 0,
                    "signal": 0,
                    "station": 0,
                    "tram": 0,
                    "water": 0
                },
                "name": "Chopper",
                "type": "ai",
                "vehicles": {
                    "aircraft": 0,
                    "roadveh": 0,
                    "ship": 0,
                    "train": 0
                },
                "version": 10
            }
        },
        "grfs": {
            "54525531": {
                "blitter": "unset",
                "flags": [],
                "md5sum": "3e7ce8d4c9d315257eb9cbe6d347ac9f",
                "palette": "dos",
                "parameters": [],
                "status": "disabled"
            },
            "f1250007": {
                "blitter": "unset",
                "flags": [],
                "md5sum": "e3933f6f3ff709ca300d41308b0be8bd",
                "palette": "dos",
                "parameters": [
                    0,
                    0,
                    0,
                    0,
                    0,
                    0,
                    16,
                    150,
                    80,
                    300
                ],
                "status": "activated"
            }
        },
        "settings": {
            "ai.ai_disable_veh_aircraft": "false",
            "ai.ai_disable_veh_roadveh": "false",
            "ai.ai_disable_veh_ship": "false",
            "ai.ai_disable_veh_train": "false",
            "ai.ai_in_multiplayer": "true",
            "blitter": "",
            "construction.autoslope": "true",
            "construction.build_object_frame_burst": "2048",
            "construction.build_object_per_64k_frames": "2097152",
            "construction.build_on_slopes": "true",
            "construction.clear_frame_burst": "4096",
            "construction.clear_per_64k_frames": "4194304",
            "construction.command_pause_level": "1",
            "construction.extra_dynamite": "true",
            "construction.extra_tree_placement": "2",
            "construction.freeform_edges": "true",
            "construction.industry_platform": "1",
            "construction.map_height_limit": "30",
            "construction.max_bridge_height": "12",
            "construction.max_bridge_length": "64",
            "construction.max_tunnel_length": "64",
            "construction.raw_industry_construction": "0",
            "construction.road_stop_on_competitor_road": "true",
            "construction.road_stop_on_town_road": "true",
            "construction.terraform_frame_burst": "4096",
            "construction.terraform_per_64k_frames": "4194304",
            "construction.train_signal_side": "1",
            "construction.tree_frame_burst": "4096",
            "construction.tree_per_64k_frames": "4194304",
            "difficulty.competitor_speed": "2",
            "difficulty.competitors_interval": "1",
            "difficulty.construction_cost": "0",
            "difficulty.disasters": "false",
            "difficulty.economy": "false",
            "difficulty.industry_density": "1",
            "difficulty.initial_interest": "2",
            "difficulty.line_reverse_mode": "false",
            "difficulty.max_loan": "300000",
            "difficulty.max_no_competitors": "4",
            "difficulty.number_towns": "0",
            "difficulty.quantity_sea_lakes": "0",
            "difficulty.subsidy_duration": "1",
            "difficulty.subsidy_multiplier": "2",
            "difficulty.terrain_type": "1",
            "difficulty.town_council_tolerance": "0",
            "difficulty.vehicle_breakdowns": "1",
            "difficulty.vehicle_costs": "0",
            "display_opt": "SHOW_TOWN_NAMES|SHOW_STATION_NAMES|SHOW_SIGNS|WAYPOINTS|SHOW_COMPETITOR_SIGNS",
            "economy.allow_shares": "false",
            "economy.allow_town_level_crossings": "true",
            "economy.allow_town_roads": "true",
            "economy.bribe": "true",
            "economy.dist_local_authority": "20",
            "economy.exclusive_rights": "true",
            "economy.feeder_payment_share": "75",
            "economy.found_town": "0",
            "economy.fund_buildings": "true",
            "economy.fund_roads": "true",
            "economy.give_money": "true",
            "economy.inflation": "false",
            "economy.infrastructure_maintenance": "false",
            "economy.initial_city_size": "2",
            "economy.larger_towns": "4",
            "economy.min_years_for_shares": "6",
            "economy.mod_road_rebuild": "true",
            "economy.multiple_industry_per_town": "false",
            "economy.station_noise_level": "false",
            "economy.town_cargogen_mode": "1",
            "economy.town_growth_rate": "2",
            "economy.town_layout": "0",
            "economy.town_noise_population[0]": "800",
            "economy.town_noise_population[1]": "2000",
            "economy.town_noise_population[2]": "4000",
            "economy.town_noise_population[3]": "400",
            "economy.type": "1",
            "engine_renew": "true",
            "engine_renew_money": "100000",
            "engine_renew_months": "6",
            "fullscreen": "false",
            "game_creation.amount_of_rivers": "0",
            "game_creation.custom_industry_number": "1",
            "game_creation.custom_sea_level": "1",
            "game_creation.custom_terrain_type": "30",
            "game_creation.custom_town_number": "1",
            "game_creation.desert_coverage": "50",
            "game_creation.ending_year": "2050",
            "game_creation.generation_seed": "3483665114",
            "game_creation.heightmap_height": "30",
            "game_creation.heightmap_rotation": "0",
            "game_creation.land_generator": "1",
            "game_creation.landscape": "temperate",
            "game_creation.map_x": "8",
            "game_creation.map_y": "8",
            "game_creation.min_river_length": "16",
            "game_creation.oil_refinery_limit": "32",
            "game_creation.river_route_random": "5",
            "game_creation.se_flat_world_height": "1",
            "game_creation.snow_coverage": "40",
            "game_creation.snow_line_height": "10",
            "game_creation.starting_year": "1950",
            "game_creation.tgen_smoothness": "1",
            "game_creation.town_name": "english",
            "game_creation.tree_placer": "2",
            "game_creation.variety": "0",
            "game_creation.water_borders": "15",
            "graphicsset": "",
            "gui.advanced_vehicle_list": "1",
            "gui.ai_developer_tools": "false",
            "gui.auto_euro": "true",
            "gui.auto_remove_signals": "false",
            "gui.auto_scrolling": "0",
            "gui.autosave": "monthly",
            "gui.autosave_on_exit": "false",
            "gui.autosave_on_network_disconnect": "true",
            "gui.coloured_news_year": "2000",
            "gui.console_backlog_length": "100",
            "gui.console_backlog_timeout": "100",
            "gui.cycle_signal_types": "0",
            "gui.date_format_in_default_names": "iso",
            "gui.default_rail_type": "0",
            "gui.default_signal_type": "5",
            "gui.developer": "1",
            "gui.drag_signals_density": "4",
            "gui.drag_signals_fixed_distance": "false",
            "gui.errmsg_duration": "5",
            "gui.fast_forward_speed_limit": "2500",
            "gui.graph_line_thickness": "3",
            "gui.hover_delay_ms": "250",
            "gui.keep_all_autosave": "false",
            "gui.link_terraform_toolbar": "false",
            "gui.linkgraph_colours": "0",
            "gui.liveries": "2",
            "gui.loading_indicators": "1",
            "gui.lost_vehicle_warn": "true",
            "gui.max_num_autosaves": "16",
            "gui.measure_tooltip": "true",
            "gui.missing_strings_threshold": "25",
            "gui.network_chat_box_height": "25",
            "gui.network_chat_box_width_pct": "40",
            "gui.network_chat_timeout": "20",
            "gui.new_nonstop": "true",
            "gui.newgrf_default_palette": "1",
            "gui.newgrf_developer_tools": "true",
            "gui.newgrf_show_old_versions": "false",
            "gui.news_message_timeout": "2",
            "gui.order_review_system": "2",
            "gui.osk_activation": "double",
            "gui.pause_on_newgame": "false",
            "gui.persistent_buildingtools": "true",
            "gui.population_in_label": "true",
            "gui.prefer_teamchat": "false",
            "gui.quick_goto": "true",
            "gui.refresh_rate": "60",
            "gui.right_mouse_wnd_close": "false",
            "gui.scale_bevels": "true",
            "gui.scenario_developer": "false",
            "gui.scroll_mode": "0",
            "gui.scrollwheel_multiplier": "5",
            "gui.scrollwheel_scrolling": "0",
            "gui.semaphore_build_before": "1950",
            "gui.settings_restriction_mode": "2",
            "gui.show_cargo_in_vehicle_lists": "false",
            "gui.show_date_in_logs": "false",
            "gui.show_finances": "true",
            "gui.show_newgrf_name": "false",
            "gui.show_track_reservation": "true",
            "gui.signal_gui_mode": "0",
            "gui.smallmap_land_colour": "0",
            "gui.smooth_scroll": "false",
            "gui.sprite_zoom_min": "0",
            "gui.starting_colour": "16",
            "gui.station_dragdrop": "true",
            "gui.station_gui_group_order": "0",
            "gui.station_gui_sort_by": "0",
            "gui.station_gui_sort_order": "0",
            "gui.station_numtracks": "1",
            "gui.station_platlength": "5",
            "gui.station_show_coverage": "false",
            "gui.statusbar_pos": "1",
            "gui.stop_location": "2",
            "gui.threaded_saves": "true",
            "gui.timetable_arrival_departure": "true",
            "gui.timetable_in_ticks": "false",
            "gui.toolbar_pos": "1",
            "gui.vehicle_income_warn": "true",
            "gui.window_snap_radius": "10",
            "gui.window_soft_limit": "20",
            "gui.zoom_max": "5",
            "gui.zoom_min": "0",
            "gui_scale": "-1",
            "invisibility_options": "0",
            "keyboard": "",
            "keyboard_caps": "",
            "language": "english_US.lng",
            "large_aa": "false",
            "large_font": "",
            "large_size": "0",
            "last_newgrf_count": "20",
            "linkgraph.accuracy": "16",
            "linkgraph.demand_distance": "100",
            "linkgraph.demand_size": "100",
            "linkgraph.distribution_armoured": "0",
            "linkgraph.distribution_default": "0",
            "linkgraph.distribution_mail": "0",
            "linkgraph.distribution_pax": "0",
            "linkgraph.recalc_interval": "16",
            "linkgraph.recalc_time": "64",
            "linkgraph.short_path_saturation": "80",
            "locale.currency": "GBP",
            "locale.digit_decimal_separator": "",
            "locale.digit_group_separator": "",
            "locale.digit_group_separator_currency": "",
            "locale.units_force": "si",
            "locale.units_height": "metric",
            "locale.units_power": "metric",
            "locale.units_velocity": "gameunits",
            "locale.units_velocity_nautical": "metric",
            "locale.units_volume": "metric",
            "locale.units_weight": "metric",
            "medium_aa": "false",
            "medium_font": "",
            "medium_size": "0",
            "mono_aa": "false",
            "mono_font": "",
            "mono_size": "0",
            "music.custom_1": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0",
            "music.custom_2": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0",
            "music.effect_vol": "100",
            "music.music_vol": "50",
            "music.playing": "true",
            "music.playlist": "0",
            "music.shuffle": "false",
            "musicdriver": "",
            "musicset": "",
            "network.autoclean_companies": "false",
            "network.autoclean_novehicles": "0",
            "network.autoclean_protected": "36",
            "network.autoclean_unprotected": "12",
            "network.bytes_per_frame": "8",
            "network.bytes_per_frame_burst": "256",
            "network.commands_per_frame": "2",
            "network.frame_freq": "0",
            "network.max_clients": "25",
            "network.max_commands_in_queue": "16",
            "network.max_companies": "15",
            "network.max_download_time": "1000",
            "network.max_init_time": "100",
            "network.max_join_time": "500",
            "network.max_lag_time": "500",
            "network.max_password_time": "2000",
            "network.min_active_clients": "0",
            "network.no_http_content_downloads": "false",
            "network.pause_on_join": "true",
            "network.reload_cfg": "false",
            "network.restart_game_year": "0",
            "network.server_admin_chat": "true",
            "network.server_admin_port": "3977",
            "network.server_game_type": "local",
            "network.server_port": "3979",
            "network.sync_freq": "100",
            "network.use_relay_service": "ask",
            "news_display.acceptance": "full",
            "news_display.accident": "full",
            "news_display.accident_other": "full",
            "news_display.advice": "full",
            "news_display.arrival_other": "summarized",
            "news_display.arrival_player": "full",
            "news_display.close": "summarized",
            "news_display.company_info": "full",
            "news_display.economy": "full",
            "news_display.general": "full",
            "news_display.new_vehicles": "full",
            "news_display.open": "summarized",
            "news_display.production_nobody": "off",
            "news_display.production_other": "off",
            "news_display.production_player": "summarized",
            "news_display.subsidies": "off",
            "order.gradual_loading": "true",
            "order.improved_load": "true",
            "order.no_servicing_if_no_breakdowns": "true",
            "order.selectgoods": "true",
            "order.serviceathelipad": "true",
            "pf.forbid_90_deg": "false",
            "pf.npf.maximum_go_to_depot_penalty": "2000",
            "pf.npf.npf_buoy_penalty": "200",
            "pf.npf.npf_crossing_penalty": "300",
            "pf.npf.npf_max_search_nodes": "10000",
            "pf.npf.npf_rail_curve_penalty": "100",
            "pf.npf.npf_rail_depot_reverse_penalty": "5000",
            "pf.npf.npf_rail_firstred_exit_penalty": "10000",
            "pf.npf.npf_rail_firstred_penalty": "1000",
            "pf.npf.npf_rail_lastred_penalty": "1000",
            "pf.npf.npf_rail_pbs_cross_penalty": "300",
            "pf.npf.npf_rail_pbs_signal_back_penalty": "1500",
            "pf.npf.npf_rail_slope_penalty": "100",
            "pf.npf.npf_rail_station_penalty": "100",
            "pf.npf.npf_road_bay_occupied_penalty": "1500",
            "pf.npf.npf_road_curve_penalty": "100",
            "pf.npf.npf_road_drive_through_penalty": "800",
            "pf.npf.npf_road_dt_occupied_penalty": "800",
            "pf.npf.npf_water_curve_penalty": "100",
            "pf.path_backoff_interval": "20",
            "pf.pathfinder_for_roadvehs": "2",
            "pf.pathfinder_for_ships": "2",
            "pf.pathfinder_for_trains": "2",
            "pf.reserve_paths": "false",
            "pf.reverse_at_signals": "false",
            "pf.roadveh_queue": "true",
            "pf.wait_for_pbs_path": "30",
            "pf.wait_oneway_signal": "15",
            "pf.wait_twoway_signal": "41",
            "pf.yapf.disable_node_optimization": "false",
            "pf.yapf.max_search_nodes": "10000",
            "pf.yapf.maximum_go_to_depot_penalty": "2000",
            "pf.yapf.rail_crossing_penalty": "300",
            "pf.yapf.rail_curve45_penalty": "100",
            "pf.yapf.rail_curve90_penalty": "600",
            "pf.yapf.rail_depot_reverse_penalty": "5000",
            "pf.yapf.rail_doubleslip_penalty": "100",
            "pf.yapf.rail_firstred_exit_penalty": "10000",
            "pf.yapf.rail_firstred_penalty": "1000",
            "pf.yapf.rail_firstred_twoway_eol": "true",
            "pf.yapf.rail_lastred_exit_penalty": "10000",
            "pf.yapf.rail_lastred_penalty": "1000",
            "pf.yapf.rail_longer_platform_penalty": "800",
            "pf.yapf.rail_longer_platform_per_tile_penalty": "0",
            "pf.yapf.rail_look_ahead_max_signals": "10",
            "pf.yapf.rail_look_ahead_signal_p0": "500",
            "pf.yapf.rail_look_ahead_signal_p1": "-100",
            "pf.yapf.rail_look_ahead_signal_p2": "5",
            "pf.yapf.rail_pbs_cross_penalty": "300",
            "pf.yapf.rail_pbs_signal_back_penalty": "1500",
            "pf.yapf.rail_pbs_station_penalty": "800",
            "pf.yapf.rail_shorter_platform_penalty": "4000",
            "pf.yapf.rail_shorter_platform_per_tile_penalty": "0",
            "pf.yapf.rail_slope_penalty": "200",
            "pf.yapf.rail_station_penalty": "1000",
            "pf.yapf.road_crossing_penalty": "300",
            "pf.yapf.road_curve_penalty": "100",
            "pf.yapf.road_slope_penalty": "200",
            "pf.yapf.road_stop_bay_occupied_penalty": "1500",
            "pf.yapf.road_stop_occupied_penalty": "800",
            "pf.yapf.road_stop_penalty": "800",
            "pf.yapf.ship_curve45_penalty": "100",
            "pf.yapf.ship_curve90_penalty": "600",
            "player_face": "0",
            "prefix": "",
            "rate": "1",
            "renew_keep_length": "false",
            "resolution": "1028,990",
            "rightclick_emulate": "false",
            "savegame_format": "",
            "screenshot_format": "",
            "script.script_max_memory_megabytes": "1024",
            "script.script_max_opcode_till_suspend": "10000",
            "script.settings_profile": "easy",
            "separator": "\".\"",
            "small_aa": "false",
            "small_font": "",
            "small_size": "0",
            "sound.ambient": "true",
            "sound.click_beep": "true",
            "sound.confirm": "true",
            "sound.disaster": "true",
            "sound.new_year": "true",
            "sound.news_full": "true",
            "sound.news_ticker": "true",
            "sound.vehicle": "true",
            "sounddriver": "",
            "soundsset": "\"NoSound\"",
            "sprite_cache_size_px": "128",
            "station.adjacent_stations": "true",
            "station.distant_join_stations": "true",
            "station.modified_catchment": "true",
            "station.never_expire_airports": "false",
            "station.serve_neutral_industries": "true",
            "station.station_spread": "12",
            "suffix": "\" credits\"",
            "support8bpp": "no",
            "to_euro": "0",
            "transparency_locks": "0",
            "transparency_options": "0",
            "vehicle.disable_elrails": "false",
            "vehicle.dynamic_engines": "true",
            "vehicle.extend_vehicle_life": "0",
            "vehicle.freight_trains": "1",
            "vehicle.max_aircraft": "200",
            "vehicle.max_roadveh": "500",
            "vehicle.max_ships": "300",
            "vehicle.max_train_length": "7",
            "vehicle.max_trains": "500",
            "vehicle.never_expire_vehicles": "false",
            "vehicle.plane_crashes": "2",
            "vehicle.plane_speed": "4",
            "vehicle.road_side": "right",
            "vehicle.roadveh_acceleration_model": "1",
            "vehicle.roadveh_slope_steepness": "7",
            "vehicle.servint_aircraft": "100",
            "vehicle.servint_ispercent": "false",
            "vehicle.servint_roadveh": "150",
            "vehicle.servint_ships": "360",
            "vehicle.servint_trains": "150",
            "vehicle.smoke_amount": "1",
            "vehicle.train_acceleration_model": "1",
            "vehicle.train_slope_steepness": "3",
            "vehicle.wagon_speed_limits": "true",
            "video_hw_accel": "false",
            "video_vsync": "false",
            "videodriver": ""
        },
        "ticks": 9819,
        "time": 11
    },
    "info": {
        "configuration": {
            "blitter": "32bpp-optimized",
            "graphics_set": {
                "name": "OpenGFX",
                "version": 7938
            },
            "language": "english_US.lng",
            "music_driver": "extmidi",
            "music_set": {
                "name": "NoMusic",
                "version": 0
            },
            "network": "no",
            "sound_driver": "sdl",
            "sound_set": {
                "name": "NoSound",
                "version": 2
            },
            "video_driver": "sdl",
            "video_info": "sdl (x11)"
        },
        "font": {
            "large": "sprite",
            "medium": "sprite",
            "mono": "sprite",
            "small": "sprite"
        },
        "openttd": {
            "bits": 64,
            "build_date": "Apr 26 2023 11:15:14",
            "dedicated": "no",
            "endian": "little",
            "newgrf_version": 503344484,
            "version": "20230426-optin-survey-m213410aca8"
        },
        "os": {
            "hardware_concurrency": 12,
            "machine": "x86_64",
            "os": "Linux",
            "release": "5.15.90.1-microsoft-standard-WSL2",
            "version": "#1 SMP Fri Jan 27 02:56:13 UTC 2023"
        }
    },
    "key": "test",
    "reason": "exit",
    "uuid": "db8c8d6a2a533e3119f7cf9af9effecb"
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment