diff --git a/src/fios.cpp b/src/fios.cpp index beee4c4fe3..2eb2d7244e 100644 --- a/src/fios.cpp +++ b/src/fios.cpp @@ -229,6 +229,11 @@ static std::string FiosMakeFilename(const std::string *path, const char *name, c * @param last Last element of buffer \a buf. * @return The completed filename. */ + +std::string FiosMakeOrderListName(const char *name) +{ + return FiosMakeFilename(_fios_path, name, ".json"); +} std::string FiosMakeSavegameName(const char *name) { const char *extension = (_game_mode == GM_EDITOR) ? ".scn" : ".sav"; diff --git a/src/fios.h b/src/fios.h index cb48672b60..00e91225ec 100644 --- a/src/fios.h +++ b/src/fios.h @@ -15,6 +15,7 @@ #include "newgrf_config.h" #include "network/core/tcp_content_type.h" #include +#include /** Special values for save-load window for the data parameter of #InvalidateWindowData. */ @@ -51,7 +52,7 @@ DECLARE_ENUM_AS_BIT_SET(SortingBits) /* Variables to display file lists */ extern SortingBits _savegame_sort_order; -void ShowSaveLoadDialog(AbstractFileType abstract_filetype, SaveLoadOperation fop); +void ShowSaveLoadDialog(AbstractFileType abstract_filetype, SaveLoadOperation fop,const Vehicle * veh = nullptr); void FiosGetSavegameList(SaveLoadOperation fop, bool show_dirs, FileList &file_list); void FiosGetScenarioList(SaveLoadOperation fop, bool show_dirs, FileList &file_list); @@ -65,6 +66,8 @@ std::optional FiosGetDiskFreeSpace(const std::string &path); bool FiosDelete(const char *name); std::string FiosMakeHeightmapName(const char *name); std::string FiosMakeSavegameName(const char *name); +std::string FiosMakeOrderListName(const char *name); + FiosType FiosGetSavegameListCallback(SaveLoadOperation fop, const std::string &file, const char *ext, char *title, const char *last); FiosType FiosGetScenarioListCallback(SaveLoadOperation fop, const std::string &file, const char *ext, char *title, const char *last); diff --git a/src/fios_gui.cpp b/src/fios_gui.cpp index ce5c3e5728..f30f237746 100644 --- a/src/fios_gui.cpp +++ b/src/fios_gui.cpp @@ -29,6 +29,10 @@ #include "gamelog.h" #include "stringfilter_type.h" #include "gamelog.h" +#include "vehicle_base.h" +#include +#include +#include #include "widgets/fios_widget.h" @@ -370,6 +374,7 @@ struct SaveLoadWindow : public Window { private: static const uint EDITBOX_MAX_SIZE = 50; + const Vehicle* veh; QueryString filename_editbox; ///< Filename editbox. AbstractFileType abstract_filetype; /// Type of file to select. SaveLoadOperation fop; ///< File operation to perform. @@ -403,11 +408,13 @@ public: this->filename_editbox.text.Assign(GenerateDefaultSaveName()); } - SaveLoadWindow(WindowDesc *desc, AbstractFileType abstract_filetype, SaveLoadOperation fop) + SaveLoadWindow(WindowDesc *desc, AbstractFileType abstract_filetype, SaveLoadOperation fop,const Vehicle * veh = nullptr) : Window(desc), filename_editbox(64), abstract_filetype(abstract_filetype), fop(fop), filter_editbox(EDITBOX_MAX_SIZE) { assert(this->fop == SLO_SAVE || this->fop == SLO_LOAD); + this->veh = veh; + /* For saving, construct an initial file name. */ if (this->fop == SLO_SAVE) { switch (this->abstract_filetype) { @@ -748,9 +755,14 @@ public: ShowHeightmapLoad(); }else if (this->abstract_filetype == FT_ORDERLIST) { + std::ifstream t(this->selected->name); + std::stringstream buffer; + buffer << t.rdbuf(); + + veh->orders->FromJSONString(veh, buffer.str()); + this->Close(); - FILE *dataFile = fopen(this->selected->name, "r"); - + } else if (!_load_check_data.HasNewGrfs() || _load_check_data.grf_compatibility != GLC_NOT_FOUND || _settings_client.gui.UserIsAllowedToChangeNewGRFs()) { _switch_mode = (_game_mode == GM_EDITOR) ? SM_LOAD_SCENARIO : SM_LOAD_GAME; ClearErrorMessages(); @@ -846,6 +858,18 @@ public: case WID_SL_SAVE_GAME: // Save game /* Note, this is also called via the OSK; and we need to lower the button. */ this->HandleButtonClick(WID_SL_SAVE_GAME); + + if (this->abstract_filetype == FT_ORDERLIST) { + + std::string fileName = FiosMakeOrderListName(this->filename_editbox.text.buf); + std::ofstream output; + output.open(fileName); + output << this->veh->orders->ToJSONString(); + output.close(); + + this->Close(); + } + break; } } @@ -1085,7 +1109,7 @@ static WindowDesc _save_orderlist_dialog_desc(__FILE__, __LINE__, * @param abstract_filetype Kind of file to handle. * @param fop File operation to perform (load or save). */ -void ShowSaveLoadDialog(AbstractFileType abstract_filetype, SaveLoadOperation fop) +void ShowSaveLoadDialog(AbstractFileType abstract_filetype, SaveLoadOperation fop,const Vehicle * veh) { CloseWindowById(WC_SAVELOAD, 0); @@ -1105,5 +1129,5 @@ void ShowSaveLoadDialog(AbstractFileType abstract_filetype, SaveLoadOperation fo sld = (abstract_filetype == FT_HEIGHTMAP) ? &_load_heightmap_dialog_desc : &_load_dialog_desc; } - new SaveLoadWindow(sld, abstract_filetype, fop); + new SaveLoadWindow(sld, abstract_filetype, fop, veh); } diff --git a/src/lang/english.txt b/src/lang/english.txt index 3c6cd68de6..65a11f095a 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -5856,3 +5856,8 @@ STR_PLANE :{BLACK}{PLANE} STR_SHIP :{BLACK}{SHIP} STR_TOOLBAR_RAILTYPE_VELOCITY :{STRING} ({VELOCITY}) + +#json messages + +STR_ERROR_ORDERLIST_MALFORMED_JSON :{WHITE}Input JSON was malformed +STR_ERROR_JON :{WHITE}JSON error diff --git a/src/order_base.h b/src/order_base.h index e554bf752b..a811d29147 100644 --- a/src/order_base.h +++ b/src/order_base.h @@ -234,7 +234,7 @@ public: void MakeLabel(OrderLabelSubType subtype); std::string ToJSONString() const; - static void FromJSONString(const Vehicle * vehicle,std::string jsonSTR); + static Order FromJSONString(std::string jsonSTR); /** * Is this a 'goto' order with a real destination? diff --git a/src/order_cmd.cpp b/src/order_cmd.cpp index 2f1004421f..f3c7f60516 100644 --- a/src/order_cmd.cpp +++ b/src/order_cmd.cpp @@ -26,6 +26,7 @@ #include "core/container_func.hpp" #include "core/pool_func.hpp" #include "core/random_func.hpp" +#include "core/serialisation.hpp" #include "aircraft.h" #include "roadveh.h" #include "station_base.h" @@ -41,12 +42,14 @@ #include "train.h" #include "date_func.h" #include "3rdparty/nlohmann/json.hpp" +#include "command_aux.h" #include "table/strings.h" #include "3rdparty/robin_hood/robin_hood.h" #include <3rdparty/nlohmann/json.hpp> #include "safeguards.h" +#include /* DestinationID must be at least as large as every these below, because it can * be any of them @@ -301,29 +304,67 @@ std::string Order::ToJSONString() const return out; } -void Order::FromJSONString(const Vehicle * v,std::string jsonSTR) +Order Order::FromJSONString(std::string jsonSTR) { - - /* - this->type = (OrderType)GB(packed, 0, 8); //done - this->flags = GB(packed, 8, 16); // done - this->dest = GB(packed, 24, 16); // done - this->extra = nullptr; //masive pain ?????? - this->next = nullptr; - this->refit_cargo = CARGO_NO_REFIT; - this->occupancy = 0; - this->wait_time = 0; - this->travel_time = 0; - this->max_speed = UINT16_MAX; - */ - //hell nlohmann::json json = nlohmann::json::parse(jsonSTR); - uint64_t order_pack = json.at("packed-data").get(); - uint64_t extra = json.at("packed-data").get(); + if (!json.contains("packed-data") && json["packed_data"].is_number_integer()) { + + Order errOrder; + + errOrder.MakeLabel(OLST_TEXT); + errOrder.SetColour(COLOUR_RED); + errOrder.SetLabelText("JSON_ERR: JSON does not contain mandatory 'packed-data' field for this order"); + + return errOrder; + } - DoCommandPEx(v->tile, v->index, 0, order_pack, CMD_INSERT_ORDER | CMD_MSG(STR_ERROR_CAN_T_INSERT_NEW_ORDER), nullptr, nullptr, 0); + Order new_order = Order(json.at("packed-data").get()); + if (json.contains("destination-id") && json["destination-id"].is_number_integer()) + json["destination-id"].get_to(new_order.dest); + + + + if (json.contains("extra") && json["extra"].is_object()) { + auto &extraJson = json["extra"]; + + new_order.AllocExtraInfo(); + + if (extraJson.contains("cargo-type-flags") && extraJson["cargo-type-flags"].is_array()) + for (int i = 0; i < 64; i++) + extraJson["cargo-type-flags"][i].get_to(new_order.extra->cargo_type_flags[i]); + + if (extraJson.contains("colour")) + extraJson["colour"].get_to(new_order.extra->colour); + + if (extraJson.contains("dispatch-index")) + extraJson["dispatch-index"].get_to(new_order.extra->dispatch_index); + + if (extraJson.contains("xdata")) + extraJson["xdata"].get_to(new_order.extra->xdata); + + if (extraJson.contains("xdata2")) + extraJson["xdata2"].get_to(new_order.extra->xdata2); + + if (extraJson.contains("xflags")) + extraJson["xflags"].get_to(new_order.extra->xflags); + + } + + if (json.contains("refit-cargo")) + json["refit-cargo"].get_to(new_order.refit_cargo); + + if (json.contains("wait-time")) + json["wait-time"].get_to(new_order.wait_time); + + if (json.contains("travel-time")) + json["travel-time"].get_to(new_order.travel_time); + + if (json.contains("max-speed")) + json["max-speed"].get_to(new_order.max_speed); + + return new_order; } /** @@ -915,33 +956,39 @@ std::string OrderList::ToJSONString() } while ((o = this->GetNext(o)) != this->GetFirstOrder()); } - std::cout << std::setw(4)<< json.dump() << "\n"; return json.dump(); } void OrderList::FromJSONString(const Vehicle * v,std::string json_str) { - nlohmann::json json = nlohmann::json::parse(json_str); - //plan... painful plan - CMD_DELETE_ORDER; //for all existing orders - CMD_INSERT_ORDER; //for each order new + Order errOrder; + + errOrder.MakeLabel(OLST_TEXT); + errOrder.SetColour(COLOUR_RED); + + nlohmann::json json; + try { + json = nlohmann::json::parse(json_str); + } catch(nlohmann::json::parse_error e){ + + ShowErrorMessage(STR_ERROR_JON, STR_ERROR_ORDERLIST_MALFORMED_JSON,WL_ERROR); + return; + } - CMD_SCHEDULED_DISPATCH_ADD_NEW_SCHEDULE; - CMD_SCHEDULED_DISPATCH_ADD; - CMD_SCHEDULED_DISPATCH_RENAME_SCHEDULE; + //delete all orders before setting the new orders + DoCommandP(v->tile, v->index, v->GetNumOrders(), CMD_DELETE_ORDER | CMD_MSG(STR_ERROR_CAN_T_DELETE_THIS_ORDER)); if (json.contains("orders")) { auto &ordersJson = json["orders"]; if (ordersJson.is_array()) { for (nlohmann::json::iterator it = ordersJson.begin(); it != ordersJson.end(); ++it) { auto &orderJson = it.value(); - Order::FromJSONString(v,orderJson.dump()); - - + Order new_order = Order::FromJSONString(orderJson.dump()); + OrderID new_orderID = v->GetNumOrders(); + DoCommandPEx(v->tile, v->index, new_orderID, 0, CMD_INSERT_ORDER | CMD_MSG(STR_ERROR_CAN_T_INSERT_NEW_ORDER), nullptr, orderJson.dump().c_str(), nullptr, 0); } - } } @@ -1179,16 +1226,19 @@ uint GetOrderDistance(const Order *prev, const Order *cur, const Vehicle *v, int * - p2 = (bit 0 - 15) - the selected order (if any). If the last order is given, * the order will be inserted before that one * @param p3 packed order to insert - * @param text unused + * @param orderJson is an optional field for an Order object encoded as JSON * @return the cost of this operation or an error */ -CommandCost CmdInsertOrder(TileIndex tile, DoCommandFlag flags, uint32_t p1, uint32_t p2, uint64_t p3, const char *text, const CommandAuxiliaryBase *aux_data) +CommandCost CmdInsertOrder(TileIndex tile, DoCommandFlag flags, uint32_t p1, uint32_t p2, uint64_t p3, const char *orderJson, const CommandAuxiliaryBase *aux_data) { - VehicleID veh = GB(p1, 0, 20); - VehicleOrderID sel_ord = GB(p2, 0, 16); - Order new_order(p3); + Order new_order = (orderJson != nullptr) ? Order::FromJSONString(orderJson) : Order(p3); - return CmdInsertOrderIntl(flags, Vehicle::GetIfValid(veh), sel_ord, new_order, false); + VehicleOrderID sel_ord = GB(p2, 0, 16); + VehicleID veh = GB(p1, 0, 20); + + CommandCost ret = CmdInsertOrderIntl(flags, Vehicle::GetIfValid(veh), sel_ord, new_order, false); + + return ret; } /** diff --git a/src/order_gui.cpp b/src/order_gui.cpp index a0ac9c36bb..220c4ec3fb 100644 --- a/src/order_gui.cpp +++ b/src/order_gui.cpp @@ -1548,6 +1548,7 @@ private: }; int selected_order; + VehicleID vehicle_id; VehicleOrderID order_over; ///< Order over which another order is dragged, \c INVALID_VEH_ORDER_ID if none. OrderPlaceObjectState goto_type; Scrollbar *vscroll; @@ -3491,89 +3492,8 @@ public: switch (index) { case 0: this->OrderClick_ReverseOrderList(0); break; case 1: this->OrderClick_ReverseOrderList(1); break; - case 2: this->vehicle->orders->ToJSONString(); break; - case 3: this->vehicle->orders->FromJSONString(this->vehicle, R"({ - "head": { - "scheduled-dispatch": [ - { - "duration": 106560, - "flags": 0, - "max-delay": 0, - "name": "alfredo", - "slots": [ - { - "flags": 0, - "offset": 4514 - }, - { - "flags": 0, - "offset": 90280 - } - ], - "start-tick": 16729920 - } - ] - }, - "orders": [ - { - "destination-id": 2, - "destination-name": "Grateley Halt", - "max-speed": 65535, - "packed-data": 33554513, - "refit-cargo": 254, - "travel-time": 0, - "wait-time": 0 - }, - { - "destination-id": 3, - "destination-name": "Grateley Exchange", - "max-speed": 65535, - "packed-data": 50331729, - "refit-cargo": 254, - "travel-time": 0, - "wait-time": 0 - }, - { - "destination-id": 4, - "destination-name": "Grateley Annexe", - "max-speed": 65535, - "packed-data": 67108945, - "refit-cargo": 254, - "travel-time": 0, - "wait-time": 0 - }, - { - "destination-id": 3, - "destination-name": "Grateley Exchange", - "max-speed": 65535, - "packed-data": 50331729, - "refit-cargo": 254, - "travel-time": 0, - "wait-time": 0 - }, - { - "destination-id": 2, - "destination-name": "Grateley Halt", - "max-speed": 65535, - "packed-data": 33554513, - "refit-cargo": 254, - "travel-time": 0, - "wait-time": 0 - }, - { - "destination-id": 1, - "destination-name": "Grateley Transfer", - "max-speed": 65535, - "packed-data": 16777297, - "refit-cargo": 254, - "travel-time": 0, - "wait-time": 0 - } - ] -})"); break; - - //case 2: ShowSaveLoadDialog(FT_ORDERLIST, SLO_SAVE); break; - //case 3: ShowSaveLoadDialog(FT_ORDERLIST, SLO_LOAD); break; + case 2: ShowSaveLoadDialog(FT_ORDERLIST, SLO_SAVE, this->GetVehicle()); break; + case 3: ShowSaveLoadDialog(FT_ORDERLIST, SLO_LOAD, this->GetVehicle()); break; default: NOT_REACHED(); } break; diff --git a/src/script/api/script_types.hpp b/src/script/api/script_types.hpp index 86e13b7cb8..f0a5b8f4c6 100644 --- a/src/script/api/script_types.hpp +++ b/src/script/api/script_types.hpp @@ -86,7 +86,7 @@ /* Define all types here, so we don't have to include the whole _type.h maze */ typedef uint BridgeType; ///< Internal name, not of any use for you. -typedef byte CargoID; ///< The ID of a cargo. +typedef uint8_t CargoID; ///< The ID of a cargo. class CommandCost; ///< The cost of a command. typedef uint16_t EngineID; ///< The ID of an engine. typedef uint16_t GoalID; ///< The ID of a goal.