/** * Adds an entry to a plugin data store. * * @param type[in] The store type. * @param pluginID[in] The plugin identifier. * @param storeptr[in,out] A pointer to the store. The store will be initialized if necessary. * @param data[in] The data entry to add. * @param classid[in] The entry class identifier. * @param autofree[in] Whether the entry should be automatically freed when removed. */ void hplugins_addToHPData(enum HPluginDataTypes type, uint32 pluginID, struct hplugin_data_store **storeptr, void *data, uint32 classid, bool autofree) { struct hplugin_data_store *store; struct hplugin_data_entry *entry; int i; nullpo_retv(storeptr); if (!HPM->data_store_validate(type, storeptr, true)) { /* woo it failed! */ ShowError("HPM:addToHPData:%s: failed, type %u (%u|%u)\n", HPM->pid2name(pluginID), type, pluginID, classid); return; } store = *storeptr; /* duplicate check */ ARR_FIND(0, VECTOR_LENGTH(store->entries), i, VECTOR_INDEX(store->entries, i)->pluginID == pluginID && VECTOR_INDEX(store->entries, i)->classid == classid); if (i != VECTOR_LENGTH(store->entries)) { ShowError("HPM:addToHPData:%s: error! attempting to insert duplicate struct of id %u and classid %u\n", HPM->pid2name(pluginID), pluginID, classid); return; } /* hplugin_data_entry is always same size, probably better to use the ERS (with reasonable chunk size e.g. 10/25/50) */ CREATE(entry, struct hplugin_data_entry, 1); /* input */ entry->pluginID = pluginID; entry->classid = classid; entry->flag.free = autofree ? 1 : 0; entry->data = data; VECTOR_ENSURE(store->entries, 1, 1); VECTOR_PUSH(store->entries, entry); }
bool hplugins_addpacket(unsigned short cmd, unsigned short length, void (*receive) (int fd), unsigned int point, unsigned int pluginID) { struct HPluginPacket *packet; int i; if (point >= hpPHP_MAX) { ShowError("HPM->addPacket:%s: unknown point '%u' specified for packet 0x%04x (len %d)\n",HPM->pid2name(pluginID),point,cmd,length); return false; } for (i = 0; i < VECTOR_LENGTH(HPM->packets[point]); i++) { if (VECTOR_INDEX(HPM->packets[point], i).cmd == cmd ) { ShowError("HPM->addPacket:%s: can't add packet 0x%04x, already in use by '%s'!", HPM->pid2name(pluginID), cmd, HPM->pid2name(VECTOR_INDEX(HPM->packets[point], i).pluginID)); return false; } } VECTOR_ENSURE(HPM->packets[point], 1, 1); VECTOR_PUSHZEROED(HPM->packets[point]); packet = &VECTOR_LAST(HPM->packets[point]); packet->pluginID = pluginID; packet->cmd = cmd; packet->len = length; packet->receive = receive; return true; }
/** * Exports a symbol to the shared symbols list. * * @param value The symbol value. * @param name The symbol name. */ void hplugin_export_symbol(void *value, const char *name) { struct hpm_symbol *symbol = NULL; CREATE(symbol ,struct hpm_symbol, 1); symbol->name = name; symbol->ptr = value; VECTOR_ENSURE(HPM->symbols, 1, 1); VECTOR_PUSH(HPM->symbols, symbol); }
/** * Initializes the data structure for a new plugin and registers it. * * @return A (retained) pointer to the initialized data. */ struct hplugin *hplugin_create(void) { struct hplugin *plugin = NULL; CREATE(plugin, struct hplugin, 1); plugin->idx = (int)VECTOR_LENGTH(HPM->plugins); plugin->filename = NULL; VECTOR_ENSURE(HPM->plugins, 1, 1); VECTOR_PUSH(HPM->plugins, plugin); return plugin; }
/** * Saves changed achievements for a character. * @param[in] char_id character identifier. * @param[out] cp pointer to loaded achievements. * @param[in] p pointer to map-sent character achievements. * @return number of achievements saved. */ static int inter_achievement_tosql(int char_id, struct char_achievements *cp, const struct char_achievements *p) { StringBuf buf; int i = 0, rows = 0; nullpo_ret(cp); nullpo_ret(p); Assert_ret(char_id > 0); StrBuf->Init(&buf); StrBuf->Printf(&buf, "REPLACE INTO `%s` (`char_id`, `ach_id`, `completed_at`, `rewarded_at`", char_achievement_db); for (i = 0; i < MAX_ACHIEVEMENT_OBJECTIVES; i++) StrBuf->Printf(&buf, ", `obj_%d`", i); StrBuf->AppendStr(&buf, ") VALUES "); for (i = 0; i < VECTOR_LENGTH(*p); i++) { int j = 0; bool save = false; struct achievement *pa = &VECTOR_INDEX(*p, i), *cpa = NULL; ARR_FIND(0, VECTOR_LENGTH(*cp), j, ((cpa = &VECTOR_INDEX(*cp, j)) && cpa->id == pa->id)); if (j == VECTOR_LENGTH(*cp)) save = true; else if (memcmp(cpa, pa, sizeof(struct achievement)) != 0) save = true; if (save) { StrBuf->Printf(&buf, "%s('%d', '%d', '%"PRId64"', '%"PRId64"'", rows ?", ":"", char_id, pa->id, (int64)pa->completed_at, (int64)pa->rewarded_at); for (j = 0; j < MAX_ACHIEVEMENT_OBJECTIVES; j++) StrBuf->Printf(&buf, ", '%d'", pa->objective[j]); StrBuf->AppendStr(&buf, ")"); rows++; } } if (rows > 0 && SQL_ERROR == SQL->QueryStr(inter->sql_handle, StrBuf->Value(&buf))) { Sql_ShowDebug(inter->sql_handle); StrBuf->Destroy(&buf); // Destroy the buffer. return 0; } // Destroy the buffer. StrBuf->Destroy(&buf); if (rows) { ShowInfo("achievements saved for char %d (total: %d, saved: %d)\n", char_id, VECTOR_LENGTH(*p), rows); /* Sync with inter-db acheivements. */ VECTOR_CLEAR(*cp); VECTOR_ENSURE(*cp, VECTOR_LENGTH(*p), 1); VECTOR_PUSHARRAY(*cp, VECTOR_DATA(*p), VECTOR_LENGTH(*p)); } return rows; }
/** * Retrieves all achievements of a character. * @param[in] char_id character identifier. * @param[out] cp pointer to character achievements structure. * @return true on success, false on failure. */ static bool inter_achievement_fromsql(int char_id, struct char_achievements *cp) { StringBuf buf; char *data; int i = 0, num_rows = 0; nullpo_ret(cp); Assert_ret(char_id > 0); // char_achievements (`char_id`, `ach_id`, `completed_at`, `rewarded_at`, `obj_0`, `obj_2`, ...`obj_9`) StrBuf->Init(&buf); StrBuf->AppendStr(&buf, "SELECT `ach_id`, `completed_at`, `rewarded_at`"); for (i = 0; i < MAX_ACHIEVEMENT_OBJECTIVES; i++) StrBuf->Printf(&buf, ", `obj_%d`", i); StrBuf->Printf(&buf, " FROM `%s` WHERE `char_id` = '%d' ORDER BY `ach_id`", char_achievement_db, char_id); if (SQL_ERROR == SQL->QueryStr(inter->sql_handle, StrBuf->Value(&buf))) { Sql_ShowDebug(inter->sql_handle); StrBuf->Destroy(&buf); return false; } VECTOR_CLEAR(*cp); if ((num_rows = (int) SQL->NumRows(inter->sql_handle)) != 0) { int j = 0; VECTOR_ENSURE(*cp, num_rows, 1); for (i = 0; i < num_rows && SQL_SUCCESS == SQL->NextRow(inter->sql_handle); i++) { struct achievement t_ach = { 0 }; SQL->GetData(inter->sql_handle, 0, &data, NULL); t_ach.id = atoi(data); SQL->GetData(inter->sql_handle, 1, &data, NULL); t_ach.completed_at = atoi(data); SQL->GetData(inter->sql_handle, 2, &data, NULL); t_ach.rewarded_at = atoi(data); /* Objectives */ for (j = 0; j < MAX_ACHIEVEMENT_OBJECTIVES; j++) { SQL->GetData(inter->sql_handle, j + 3, &data, NULL); t_ach.objective[j] = atoi(data); } /* Add Entry */ VECTOR_PUSH(*cp, t_ach); } } SQL->FreeResult(inter->sql_handle); StrBuf->Destroy(&buf); if (num_rows > 0) ShowInfo("achievements loaded for char %d (total: %d)\n", char_id, num_rows); return true; }
/** * Creates a new console command entry. * * @param name The command name. * @param func The command callback. */ void console_parse_create(char *name, CParseFunc func) { int i; char *tok; char sublist[CP_CMD_LENGTH * 5]; struct CParseEntry *cmd; safestrncpy(sublist, name, CP_CMD_LENGTH * 5); tok = strtok(sublist,":"); ARR_FIND(0, VECTOR_LENGTH(console->input->command_list), i, strcmpi(tok, VECTOR_INDEX(console->input->command_list, i)->cmd) == 0); if (i == VECTOR_LENGTH(console->input->command_list)) { CREATE(cmd, struct CParseEntry, 1); safestrncpy(cmd->cmd, tok, CP_CMD_LENGTH); cmd->type = CPET_UNKNOWN; VECTOR_ENSURE(console->input->commands, 1, 1); VECTOR_PUSH(console->input->commands, cmd); VECTOR_ENSURE(console->input->command_list, 1, 1); VECTOR_PUSH(console->input->command_list, cmd); }
/** * Adds a configuration listener for a plugin. * * @param pluginID The plugin identifier. * @param type The configuration type to listen for. * @param name The configuration entry name. * @param func The callback function. * @retval true if the listener was added successfully. * @retval false in case of error. */ bool hplugins_addconf(unsigned int pluginID, enum HPluginConfType type, char *name, void (*parse_func) (const char *key, const char *val), int (*return_func) (const char *key)) { struct HPConfListenStorage *conf; int i; if (parse_func == NULL) { ShowError("HPM->addConf:%s: missing setter function for config '%s'\n",HPM->pid2name(pluginID),name); return false; } if (type == HPCT_BATTLE && return_func == NULL) { ShowError("HPM->addConf:%s: missing getter function for config '%s'\n",HPM->pid2name(pluginID),name); return false; } if (type >= HPCT_MAX) { ShowError("HPM->addConf:%s: unknown point '%u' specified for config '%s'\n",HPM->pid2name(pluginID),type,name); return false; } ARR_FIND(0, VECTOR_LENGTH(HPM->config_listeners[type]), i, strcmpi(name, VECTOR_INDEX(HPM->config_listeners[type], i).key) == 0); if (i != VECTOR_LENGTH(HPM->config_listeners[type])) { ShowError("HPM->addConf:%s: duplicate '%s', already in use by '%s'!", HPM->pid2name(pluginID), name, HPM->pid2name(VECTOR_INDEX(HPM->config_listeners[type], i).pluginID)); return false; } VECTOR_ENSURE(HPM->config_listeners[type], 1, 1); VECTOR_PUSHZEROED(HPM->config_listeners[type]); conf = &VECTOR_LAST(HPM->config_listeners[type]); conf->pluginID = pluginID; safestrncpy(conf->key, name, HPM_ADDCONF_LENGTH); conf->parse_func = parse_func; conf->return_func = return_func; return true; }
// Loads new mails of this char_id/account_id static int inter_rodex_fromsql(int char_id, int account_id, int8 opentype, int64 mail_id, struct rodex_maillist *mails) { int count = 0; struct rodex_message msg = { 0 }; struct SqlStmt *stmt; nullpo_retr(-1, mails); stmt = SQL->StmtMalloc(inter->sql_handle); switch (opentype) { case RODEX_OPENTYPE_MAIL: if (SQL_ERROR == SQL->StmtPrepare(stmt, "SELECT `mail_id`, `sender_name`, `sender_id`, `receiver_name`, `receiver_id`, `receiver_accountid`," "`title`, `body`, `zeny`, `type`, `is_read`, `sender_read`, `send_date`, `expire_date`, `weight`" "FROM `%s` WHERE `expire_date` > '%d' AND `receiver_id` = '%d' AND `mail_id` > '%"PRId64"'" "ORDER BY `mail_id` ASC", rodex_db, (int)time(NULL), char_id, mail_id) ) { SqlStmt_ShowDebug(stmt); SQL->StmtFree(stmt); return -1; } break; case RODEX_OPENTYPE_ACCOUNT: if (SQL_ERROR == SQL->StmtPrepare(stmt, "SELECT `mail_id`, `sender_name`, `sender_id`, `receiver_name`, `receiver_id`, `receiver_accountid`," "`title`, `body`, `zeny`, `type`, `is_read`, `sender_read`, `send_date`, `expire_date`, `weight`" "FROM `%s` WHERE " "`expire_date` > '%d' AND `receiver_accountid` = '%d' AND `mail_id` > '%"PRId64"'" "ORDER BY `mail_id` ASC", rodex_db, (int)time(NULL), account_id, mail_id) ) { SqlStmt_ShowDebug(stmt); SQL->StmtFree(stmt); return -1; } break; case RODEX_OPENTYPE_RETURN: if (SQL_ERROR == SQL->StmtPrepare(stmt, "SELECT `mail_id`, `sender_name`, `sender_id`, `receiver_name`, `receiver_id`, `receiver_accountid`," "`title`, `body`, `zeny`, `type`, `is_read`, `sender_read`, `send_date`, `expire_date`, `weight`" "FROM `%s` WHERE (`is_read` = 0 AND `sender_id` = '%d' AND `expire_date` <= '%d' AND `expire_date` + '%d' > '%d' AND `mail_id` > '%"PRId64"')" "ORDER BY `mail_id` ASC", rodex_db, char_id, (int)time(NULL), RODEX_EXPIRE, (int)time(NULL), mail_id) ) { SqlStmt_ShowDebug(stmt); SQL->StmtFree(stmt); return -1; } break; case RODEX_OPENTYPE_UNSET: if (SQL_ERROR == SQL->StmtPrepare(stmt, "SELECT `mail_id`, `sender_name`, `sender_id`, `receiver_name`, `receiver_id`, `receiver_accountid`," "`title`, `body`, `zeny`, `type`, `is_read`, `sender_read`, `send_date`, `expire_date`, `weight`" "FROM `%s` WHERE " "((`expire_date` > '%d' AND (`receiver_id` = '%d' OR `receiver_accountid` = '%d'))" "OR (`is_read` = 0 AND `sender_id` = '%d' AND `expire_date` <= '%d' AND `expire_date` + '%d' > '%d'))" "ORDER BY `mail_id` ASC", rodex_db, (int)time(NULL), char_id, account_id, char_id, (int)time(NULL), RODEX_EXPIRE, (int)time(NULL)) ) { SqlStmt_ShowDebug(stmt); SQL->StmtFree(stmt); return -1; } break; } if (SQL_ERROR == SQL->StmtExecute(stmt) || SQL_ERROR == SQL->StmtBindColumn(stmt, 0, SQLDT_INT64, &msg.id, sizeof msg.id, NULL, NULL) || SQL_ERROR == SQL->StmtBindColumn(stmt, 1, SQLDT_STRING, &msg.sender_name, sizeof msg.sender_name, NULL, NULL) || SQL_ERROR == SQL->StmtBindColumn(stmt, 2, SQLDT_INT, &msg.sender_id, sizeof msg.sender_id, NULL, NULL) || SQL_ERROR == SQL->StmtBindColumn(stmt, 3, SQLDT_STRING, &msg.receiver_name, sizeof msg.receiver_name, NULL, NULL) || SQL_ERROR == SQL->StmtBindColumn(stmt, 4, SQLDT_INT, &msg.receiver_id, sizeof msg.receiver_id, NULL, NULL) || SQL_ERROR == SQL->StmtBindColumn(stmt, 5, SQLDT_INT, &msg.receiver_accountid, sizeof msg.receiver_accountid, NULL, NULL) || SQL_ERROR == SQL->StmtBindColumn(stmt, 6, SQLDT_STRING, &msg.title, sizeof msg.title, NULL, NULL) || SQL_ERROR == SQL->StmtBindColumn(stmt, 7, SQLDT_STRING, &msg.body, sizeof msg.body, NULL, NULL) || SQL_ERROR == SQL->StmtBindColumn(stmt, 8, SQLDT_INT64, &msg.zeny, sizeof msg.zeny, NULL, NULL) || SQL_ERROR == SQL->StmtBindColumn(stmt, 9, SQLDT_UINT8, &msg.type, sizeof msg.type, NULL, NULL) || SQL_ERROR == SQL->StmtBindColumn(stmt, 10, SQLDT_BOOL, &msg.is_read, sizeof msg.is_read, NULL, NULL) || SQL_ERROR == SQL->StmtBindColumn(stmt, 11, SQLDT_BOOL, &msg.sender_read, sizeof msg.sender_read, NULL, NULL) || SQL_ERROR == SQL->StmtBindColumn(stmt, 12, SQLDT_INT, &msg.send_date, sizeof msg.send_date, NULL, NULL) || SQL_ERROR == SQL->StmtBindColumn(stmt, 13, SQLDT_INT, &msg.expire_date, sizeof msg.expire_date, NULL, NULL) || SQL_ERROR == SQL->StmtBindColumn(stmt, 14, SQLDT_INT, &msg.weight, sizeof msg.weight, NULL, NULL) ) { SqlStmt_ShowDebug(stmt); SQL->StmtFree(stmt); return -1; } { struct item it = { 0 }; StringBuf buf; struct SqlStmt *stmt_items = SQL->StmtMalloc(inter->sql_handle); int i; if (stmt_items == NULL) { SQL->StmtFreeResult(stmt); SQL->StmtFree(stmt); return -1; } StrBuf->Init(&buf); StrBuf->AppendStr(&buf, "SELECT `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`, `bound`, `unique_id`"); for (i = 0; i < MAX_SLOTS; i++) { StrBuf->Printf(&buf, ", `card%d`", i); } for (i = 0; i < MAX_ITEM_OPTIONS; i++) { StrBuf->Printf(&buf, ", `opt_idx%d`, `opt_val%d`", i, i); } StrBuf->Printf(&buf, "FROM `%s` WHERE mail_id = ? ORDER BY `mail_id` ASC", rodex_item_db); if (SQL_ERROR == SQL->StmtPrepareStr(stmt_items, StrBuf->Value(&buf)) || SQL_ERROR == SQL->StmtBindParam(stmt_items, 0, SQLDT_INT64, &msg.id, sizeof msg.id) ) { SqlStmt_ShowDebug(stmt_items); } // Read mails while (SQL_SUCCESS == SQL->StmtNextRow(stmt)) { if (msg.type & MAIL_TYPE_ITEM) { if (SQL_ERROR == SQL->StmtExecute(stmt_items) || SQL_ERROR == SQL->StmtBindColumn(stmt_items, 0, SQLDT_INT, &it.nameid, sizeof it.nameid, NULL, NULL) || SQL_ERROR == SQL->StmtBindColumn(stmt_items, 1, SQLDT_SHORT, &it.amount, sizeof it.amount, NULL, NULL) || SQL_ERROR == SQL->StmtBindColumn(stmt_items, 2, SQLDT_UINT, &it.equip, sizeof it.equip, NULL, NULL) || SQL_ERROR == SQL->StmtBindColumn(stmt_items, 3, SQLDT_CHAR, &it.identify, sizeof it.identify, NULL, NULL) || SQL_ERROR == SQL->StmtBindColumn(stmt_items, 4, SQLDT_CHAR, &it.refine, sizeof it.refine, NULL, NULL) || SQL_ERROR == SQL->StmtBindColumn(stmt_items, 5, SQLDT_CHAR, &it.attribute, sizeof it.attribute, NULL, NULL) || SQL_ERROR == SQL->StmtBindColumn(stmt_items, 6, SQLDT_UINT, &it.expire_time, sizeof it.expire_time, NULL, NULL) || SQL_ERROR == SQL->StmtBindColumn(stmt_items, 7, SQLDT_UCHAR, &it.bound, sizeof it.bound, NULL, NULL) || SQL_ERROR == SQL->StmtBindColumn(stmt_items, 8, SQLDT_UINT64, &it.unique_id, sizeof it.unique_id, NULL, NULL) ) { SqlStmt_ShowDebug(stmt_items); } for (i = 0; i < MAX_SLOTS; i++) { if (SQL_ERROR == SQL->StmtBindColumn(stmt_items, 9 + i, SQLDT_INT, &it.card[i], sizeof it.card[i], NULL, NULL)) SqlStmt_ShowDebug(stmt_items); } for (i = 0; i < MAX_ITEM_OPTIONS; i++) { if (SQL_ERROR == SQL->StmtBindColumn(stmt_items, 9 + MAX_SLOTS + i * 2, SQLDT_INT16, &it.option[i].index, sizeof it.option[i].index, NULL, NULL) || SQL_ERROR == SQL->StmtBindColumn(stmt_items, 10 + MAX_SLOTS + i * 2, SQLDT_INT16, &it.option[i].value, sizeof it.option[i].value, NULL, NULL) ) { SqlStmt_ShowDebug(stmt_items); } } for (i = 0; i < RODEX_MAX_ITEM && SQL_SUCCESS == SQL->StmtNextRow(stmt_items); ++i) { msg.items[i].item = it; msg.items_count++; } } if (msg.items_count == 0) { msg.type &= ~MAIL_TYPE_ITEM; } if (msg.zeny == 0) { msg.type &= ~MAIL_TYPE_ZENY; } #if PACKETVER >= 20170419 if (opentype == RODEX_OPENTYPE_UNSET) { if (msg.receiver_id == 0) msg.opentype = RODEX_OPENTYPE_ACCOUNT; else if (msg.expire_date < time(NULL)) msg.opentype = RODEX_OPENTYPE_RETURN; else msg.opentype = RODEX_OPENTYPE_MAIL; } else { msg.opentype = opentype; } #else msg.opentype = opentype; #endif #if PACKETVER < 20160601 // NPC Message Type isn't supported in old clients msg.type &= ~MAIL_TYPE_NPC; #endif ++count; VECTOR_ENSURE(*mails, 1, 1); VECTOR_PUSH(*mails, msg); memset(&msg, 0, sizeof(struct rodex_message)); SQL->StmtFreeResult(stmt_items); } StrBuf->Destroy(&buf); SQL->StmtFree(stmt_items); } SQL->StmtFreeResult(stmt); SQL->StmtFree(stmt); ShowInfo("rodex load complete from DB - id: %d (total: %d)\n", char_id, count); return count; }
/** * Loads console commands list **/ void console_load_defaults(void) { /** * Defines a main category. * * Categories can't be used as commands! * E.G. * - sql update skip * 'sql' is the main category * CP_DEF_C(category) **/ #define CP_DEF_C(x) { #x , CPET_CATEGORY, NULL , NULL, NULL } /** * Defines a sub-category. * * Sub-categories can't be used as commands! * E.G. * - sql update skip * 'update' is a sub-category * CP_DEF_C2(command, category) **/ #define CP_DEF_C2(x,y) { #x , CPET_CATEGORY, NULL , #y, NULL } /** * Defines a command that is inside a category or sub-category * CP_DEF_S(command, category/sub-category) **/ #define CP_DEF_S(x,y) { #x, CPET_FUNCTION, CPCMD_C_A(x,y), #y, NULL } /** * Defines a command that is _not_ inside any category * CP_DEF_S(command) **/ #define CP_DEF(x) { #x , CPET_FUNCTION, CPCMD_A(x), NULL, NULL } struct { char *name; int type; CParseFunc func; char *connect; struct CParseEntry *self; } default_list[] = { CP_DEF(help), /** * Server related commands **/ CP_DEF_C(server), CP_DEF_S(ers_report,server), CP_DEF_S(mem_report,server), CP_DEF_S(malloc_usage,server), CP_DEF_S(exit,server), /** * Sql related commands **/ CP_DEF_C(sql), CP_DEF_C2(update,sql), CP_DEF_S(skip,update), }; int len = ARRAYLENGTH(default_list); struct CParseEntry *cmd; int i; VECTOR_ENSURE(console->input->commands, len, 1); for(i = 0; i < len; i++) { CREATE(cmd, struct CParseEntry, 1); safestrncpy(cmd->cmd, default_list[i].name, CP_CMD_LENGTH); cmd->type = default_list[i].type; switch (cmd->type) { case CPET_FUNCTION: cmd->u.func = default_list[i].func; break; case CPET_CATEGORY: VECTOR_INIT(cmd->u.children); break; case CPET_UNKNOWN: break; } VECTOR_PUSH(console->input->commands, cmd); default_list[i].self = cmd; if (!default_list[i].connect) { VECTOR_ENSURE(console->input->command_list, 1, 1); VECTOR_PUSH(console->input->command_list, cmd); } } for (i = 0; i < len; i++) { int k; if (!default_list[i].connect) continue; ARR_FIND(0, VECTOR_LENGTH(console->input->commands), k, strcmpi(default_list[i].connect, VECTOR_INDEX(console->input->commands, k)->cmd) == 0); if (k != VECTOR_LENGTH(console->input->commands)) { struct CParseEntry *parent = VECTOR_INDEX(console->input->commands, k); Assert_retb(parent->type == CPET_CATEGORY); cmd = default_list[i].self; VECTOR_ENSURE(parent->u.children, 1, 1); VECTOR_PUSH(parent->u.children, cmd); } } #undef CP_DEF_C #undef CP_DEF_C2 #undef CP_DEF_S #undef CP_DEF }