Clément Wolf 2 недель назад
Родитель
Сommit
4aa4260e5b

+ 11 - 11
src/App/Osu/Bancho.cpp

@@ -504,13 +504,13 @@ Packet build_login_packet() {
     Packet packet;
 
     write_bytes(&packet, (u8 *)bancho.username.toUtf8(), bancho.username.lengthUtf8());
-    write_u8(&packet, '\n');
+    write<u8>(&packet, '\n');
 
     write_bytes(&packet, (u8 *)bancho.pw_md5.hash, 32);
-    write_u8(&packet, '\n');
+    write<u8>(&packet, '\n');
 
     write_bytes(&packet, (u8 *)OSU_VERSION, strlen(OSU_VERSION));
-    write_u8(&packet, '|');
+    write<u8>(&packet, '|');
 
     // UTC offset
     time_t now = time(NULL);
@@ -518,15 +518,15 @@ Packet build_login_packet() {
     auto local_time = localtime(&now);
     int utc_offset = difftime(mktime(local_time), mktime(gmt)) / 3600;
     if(utc_offset < 0) {
-        write_u8(&packet, '-');
+        write<u8>(&packet, '-');
         utc_offset *= -1;
     }
-    write_u8(&packet, '0' + utc_offset);
-    write_u8(&packet, '|');
+    write<u8>(&packet, '0' + utc_offset);
+    write<u8>(&packet, '|');
 
     // Don't dox the user's city
-    write_u8(&packet, '0');
-    write_u8(&packet, '|');
+    write<u8>(&packet, '0');
+    write<u8>(&packet, '|');
 
     char osu_path[PATH_MAX] = {0};
 #ifdef _WIN32
@@ -556,9 +556,9 @@ Packet build_login_packet() {
     write_bytes(&packet, (u8 *)bancho.client_hashes.toUtf8(), bancho.client_hashes.lengthUtf8());
 
     // Allow PMs from strangers
-    write_u8(&packet, '|');
-    write_u8(&packet, '0');
+    write<u8>(&packet, '|');
+    write<u8>(&packet, '0');
 
-    write_u8(&packet, '\n');
+    write<u8>(&packet, '\n');
     return packet;
 }

+ 12 - 13
src/App/Osu/BanchoNetworking.cpp

@@ -51,9 +51,9 @@ void disconnect() {
     // This is a blocking call, but we *do* want this to block when quitting the game.
     if(bancho.is_online()) {
         Packet packet;
-        write_u16(&packet, LOGOUT);
-        write_u8(&packet, 0);
-        write_u32(&packet, 0);
+        write<u16>(&packet, LOGOUT);
+        write<u8>(&packet, 0);
+        write<u32>(&packet, 0);
 
         CURL *curl = curl_easy_init();
         auto version_header = UString::format("x-mcosu-ver: %s", bancho.neosu_version.toUtf8());
@@ -309,7 +309,6 @@ static void *do_networking(void *data) {
 
     last_packet_tms = time(NULL);
 
-    curl_global_init(CURL_GLOBAL_ALL);
     CURL *curl = curl_easy_init();
     if(!curl) {
         debugLog("Failed to initialize cURL, online functionality disabled.\n");
@@ -343,9 +342,9 @@ static void *do_networking(void *data) {
             free(login.memory);
             pthread_mutex_lock(&outgoing_mutex);
         } else if(should_ping && outgoing.pos == 0) {
-            write_u16(&outgoing, PING);
-            write_u8(&outgoing, 0);
-            write_u32(&outgoing, 0);
+            write<u16>(&outgoing, PING);
+            write<u8>(&outgoing, 0);
+            write<u32>(&outgoing, 0);
 
             // Polling gets slower over time, but resets when we receive new data
             if(seconds_between_pings < 30.0) {
@@ -360,9 +359,9 @@ static void *do_networking(void *data) {
 
             // DEBUG: If we're not sending the right amount of bytes, bancho.py just
             // chugs along! To try to detect it faster, we'll send two packets per request.
-            write_u16(&out, PING);
-            write_u8(&out, 0);
-            write_u32(&out, 0);
+            write<u16>(&out, PING);
+            write<u8>(&out, 0);
+            write<u32>(&out, 0);
 
             send_bancho_packet(curl, out);
             free(out.memory);
@@ -495,9 +494,9 @@ void send_packet(Packet &packet) {
 
     // We're not sending it immediately, instead we just add it to the pile of
     // packets to send
-    write_u16(&outgoing, packet.id);
-    write_u8(&outgoing, 0);
-    write_u32(&outgoing, packet.pos);
+    write<u16>(&outgoing, packet.id);
+    write<u8>(&outgoing, 0);
+    write<u32>(&outgoing, packet.pos);
     write_bytes(&outgoing, packet.memory, packet.pos);
 
     pthread_mutex_unlock(&outgoing_mutex);

+ 27 - 33
src/App/Osu/BanchoProtocol.cpp

@@ -65,39 +65,39 @@ Room::Room(Packet *packet) {
 }
 
 void Room::pack(Packet *packet) {
-    write_u16(packet, id);
-    write_u8(packet, in_progress);
-    write_u8(packet, match_type);
-    write_u32(packet, mods);
+    write<u16>(packet, id);
+    write<u8>(packet, in_progress);
+    write<u8>(packet, match_type);
+    write<u32>(packet, mods);
     write_string(packet, name.toUtf8());
     write_string(packet, password.toUtf8());
     write_string(packet, map_name.toUtf8());
-    write_u32(packet, map_id);
+    write<u32>(packet, map_id);
     write_string(packet, map_md5.toUtf8());
     for(int i = 0; i < 16; i++) {
-        write_u8(packet, slots[i].status);
+        write<u8>(packet, slots[i].status);
     }
     for(int i = 0; i < 16; i++) {
-        write_u8(packet, slots[i].team);
+        write<u8>(packet, slots[i].team);
     }
     for(int s = 0; s < 16; s++) {
         if(slots[s].has_player()) {
-            write_u32(packet, slots[s].player_id);
+            write<u32>(packet, slots[s].player_id);
         }
     }
 
-    write_u32(packet, host_id);
-    write_u8(packet, mode);
-    write_u8(packet, win_condition);
-    write_u8(packet, team_type);
-    write_u8(packet, freemods);
+    write<u32>(packet, host_id);
+    write<u8>(packet, mode);
+    write<u8>(packet, win_condition);
+    write<u8>(packet, team_type);
+    write<u8>(packet, freemods);
     if(freemods) {
         for(int i = 0; i < 16; i++) {
-            write_u32(packet, slots[i].mods);
+            write<u32>(packet, slots[i].mods);
         }
     }
 
-    write_u32(packet, seed);
+    write<u32>(packet, seed);
 }
 
 bool Room::is_host() { return host_id == bancho.user_id; }
@@ -182,8 +182,8 @@ void skip_string(Packet *packet) {
 
 void write_bytes(Packet *packet, u8 *bytes, size_t n) {
     if(packet->pos + n > packet->size) {
-        packet->memory = (unsigned char *)realloc(packet->memory, packet->size + n + 128);
-        packet->size += n + 128;
+        packet->memory = (unsigned char *)realloc(packet->memory, packet->size + n + 4096);
+        packet->size += n + 4096;
         if(!packet->memory) return;
     }
 
@@ -191,18 +191,10 @@ void write_bytes(Packet *packet, u8 *bytes, size_t n) {
     packet->pos += n;
 }
 
-void write_u8(Packet *packet, u8 b) { write_bytes(packet, &b, 1); }
-
-void write_u16(Packet *packet, u16 s) { write_bytes(packet, (u8 *)&s, 2); }
-
-void write_u32(Packet *packet, u32 i) { write_bytes(packet, (u8 *)&i, 4); }
-
-void write_u64(Packet *packet, u64 i) { write_bytes(packet, (u8 *)&i, 8); }
-
 void write_uleb128(Packet *packet, u32 num) {
     if(num == 0) {
         u8 zero = 0;
-        write_u8(packet, zero);
+        write<u8>(packet, zero);
         return;
     }
 
@@ -212,25 +204,27 @@ void write_uleb128(Packet *packet, u32 num) {
         if(num != 0) {
             next |= 0x80;
         }
-        write_u8(packet, next);
+        write<u8>(packet, next);
     }
 }
 
-void write_f32(Packet *packet, float f) { write_bytes(packet, (u8 *)&f, 4); }
-
-void write_f64(Packet *packet, double f) { write_bytes(packet, (u8 *)&f, 8); }
-
 void write_string(Packet *packet, const char *str) {
     if(!str || str[0] == '\0') {
         u8 zero = 0;
-        write_u8(packet, zero);
+        write<u8>(packet, zero);
         return;
     }
 
     u8 empty_check = 11;
-    write_u8(packet, empty_check);
+    write<u8>(packet, empty_check);
 
     u32 len = strlen(str);
     write_uleb128(packet, len);
     write_bytes(packet, (u8 *)str, len);
 }
+
+void write_hash(Packet *packet, MD5Hash hash) {
+    write<u8>(packet, 0x0B);
+    write<u8>(packet, 0x20);
+    write_bytes(packet, (u8 *)hash.hash, 32);
+}

+ 9 - 8
src/App/Osu/BanchoProtocol.h

@@ -141,6 +141,12 @@ struct Packet {
     size_t pos = 0;
     u8 *extra = nullptr;
     u32 extra_int = 0;  // lazy
+
+    void reserve(u32 newsize) {
+        if(newsize <= size) return;
+        memory = (u8 *)realloc(memory, newsize);
+        size = newsize;
+    }
 };
 
 struct Slot {
@@ -268,20 +274,15 @@ T read(Packet *packet) {
 }
 
 void write_bytes(Packet *packet, u8 *bytes, size_t n);
-void write_u8(Packet *packet, u8 b);
-void write_u16(Packet *packet, u16 s);
-void write_u32(Packet *packet, u32 i);
-void write_u64(Packet *packet, u64 i);
-void write_f32(Packet *packet, f32 f);
-void write_f64(Packet *packet, f64 f);
 void write_uleb128(Packet *packet, u32 num);
 void write_string(Packet *packet, const char *str);
+void write_hash(Packet *packet, MD5Hash hash);
 
 template <typename T>
 void write(Packet *packet, T t) {
     if(packet->pos + sizeof(T) > packet->size) {
-        packet->memory = (u8 *)realloc(packet->memory, packet->size + sizeof(T) + 128);
-        packet->size += sizeof(T) + 128;
+        packet->memory = (u8 *)realloc(packet->memory, packet->size + sizeof(T) + 4096);
+        packet->size += sizeof(T) + 4096;
         if(!packet->memory) return;
     }
 

+ 1 - 2
src/App/Osu/Beatmap.cpp

@@ -1726,11 +1726,10 @@ void Beatmap::loadMusic(bool stream, bool prescan) {
 
 void Beatmap::unloadMusic() {
     if(m_osu->getInstanceID() < 2) {
-        engine->getSound()->stop(m_music);
         engine->getResourceManager()->destroyResource(m_music);
     }
 
-    m_music = NULL;
+    m_music = nullptr;
 }
 
 void Beatmap::unloadObjects() {

+ 1 - 1
src/App/Osu/Chat.cpp

@@ -245,7 +245,7 @@ void Chat::onKeyDown(KeyboardEvent &key) {
             write_string(&packet, (char *)bancho.username.toUtf8());
             write_string(&packet, (char *)m_input_box->getText().toUtf8());
             write_string(&packet, (char *)m_selected_channel->name.toUtf8());
-            write_u32(&packet, bancho.user_id);
+            write<u32>(&packet, bancho.user_id);
             send_packet(packet);
 
             // Server doesn't echo the message back

+ 4 - 4
src/App/Osu/Collections.cpp

@@ -208,22 +208,22 @@ bool save_collections() {
     const double startTime = engine->getTimeReal();
 
     Packet db;
-    write_u32(&db, COLLECTIONS_DB_VERSION);
+    write<u32>(&db, COLLECTIONS_DB_VERSION);
 
     u32 nb_collections = collections.size();
-    write_u32(&db, nb_collections);
+    write<u32>(&db, nb_collections);
 
     for(auto collection : collections) {
         write_string(&db, collection->name.c_str());
 
         u32 nb_deleted = collection->deleted_maps.size();
-        write_u32(&db, nb_deleted);
+        write<u32>(&db, nb_deleted);
         for(auto map : collection->deleted_maps) {
             write_string(&db, map.hash);
         }
 
         u32 nb_neosu = collection->neosu_maps.size();
-        write_u32(&db, nb_neosu);
+        write<u32>(&db, nb_neosu);
         for(auto map : collection->neosu_maps) {
             write_string(&db, map.hash);
         }

+ 48 - 37
src/App/Osu/Database.cpp

@@ -294,8 +294,10 @@ class DatabaseLoader : public Resource {
             m_toCleanup.swap(m_db->m_databaseBeatmaps);
             m_db->m_databaseBeatmaps.clear();
             m_db->loadDB(&db, m_bNeedRawLoad);
-        } else
+        } else {
             m_bNeedRawLoad = true;
+        }
+        free(db.memory);
 
         m_bAsyncReady = true;
     }
@@ -368,6 +370,8 @@ Database::~Database() {
     for(int i = 0; i < m_scoreSortingMethods.size(); i++) {
         delete m_scoreSortingMethods[i].comparator;
     }
+
+    unload_collections();
 }
 
 void Database::update() {
@@ -1393,6 +1397,8 @@ void Database::loadStars() {
 
 void Database::saveStars() {
     debugLog("Osu: Saving stars ...\n");
+    Timer t;
+    t.start();
 
     i64 numStarsCacheEntries = 0;
     for(DatabaseBeatmap *beatmap : m_databaseBeatmaps) {
@@ -1409,13 +1415,15 @@ void Database::saveStars() {
 
     // write
     Packet cache;
-    write_u32(&cache, STARS_CACHE_VERSION);
+    write<u32>(&cache, STARS_CACHE_VERSION);
     write_string(&cache, "00000000000000000000000000000000");
-    write_u64(&cache, numStarsCacheEntries);
+    write<u64>(&cache, numStarsCacheEntries);
+
+    cache.reserve(cache.size + (34 + 4 + 4 + 4 + 4) * numStarsCacheEntries);
     for(DatabaseBeatmap *beatmap : m_databaseBeatmaps) {
         for(DatabaseBeatmap *diff2 : beatmap->getDifficulties()) {
-            write_string(&cache, diff2->getMD5Hash().hash);
-            write_f32(&cache, diff2->getStarsNomod());
+            write_hash(&cache, diff2->getMD5Hash().hash);
+            write<f32>(&cache, diff2->getStarsNomod());
             write<i32>(&cache, diff2->getMinBPM());
             write<i32>(&cache, diff2->getMaxBPM());
             write<i32>(&cache, diff2->getMostCommonBPM());
@@ -1427,6 +1435,9 @@ void Database::saveStars() {
     }
 
     free(cache.memory);
+
+    t.update();
+    debugLog("Saving stars took %f seconds.\n", t.getElapsedTime());
 }
 
 void Database::loadScores() {
@@ -1704,8 +1715,8 @@ void Database::saveScores() {
 
     // write header
     Packet db;
-    write_u32(&db, LiveScore::VERSION);
-    write_u32(&db, numBeatmaps);
+    write<u32>(&db, LiveScore::VERSION);
+    write<u32>(&db, numBeatmaps);
 
     // write scores for each beatmap
     for(auto &beatmap : m_scores) {
@@ -1717,55 +1728,55 @@ void Database::saveScores() {
         if(numNonLegacyScores == 0) continue;
 
         write_string(&db, beatmap.first.hash);  // beatmap md5 hash
-        write_u32(&db, numNonLegacyScores);     // numScores
+        write<u32>(&db, numNonLegacyScores);    // numScores
 
         for(auto &score : beatmap.second) {
             if(score.isLegacyScore) continue;
 
             u8 gamemode = 0;
             if(score.version > 20190103 && score.isImportedLegacyScore) gamemode = hackIsImportedLegacyScoreFlag;
-            write_u8(&db, gamemode);
+            write<u8>(&db, gamemode);
 
-            write_u32(&db, score.version);
-            write_u64(&db, score.unixTimestamp);
+            write<u32>(&db, score.version);
+            write<u64>(&db, score.unixTimestamp);
 
             // default
             write_string(&db, score.playerName.c_str());
 
-            write_u16(&db, score.num300s);
-            write_u16(&db, score.num100s);
-            write_u16(&db, score.num50s);
-            write_u16(&db, score.numGekis);
-            write_u16(&db, score.numKatus);
-            write_u16(&db, score.numMisses);
+            write<u16>(&db, score.num300s);
+            write<u16>(&db, score.num100s);
+            write<u16>(&db, score.num50s);
+            write<u16>(&db, score.numGekis);
+            write<u16>(&db, score.numKatus);
+            write<u16>(&db, score.numMisses);
 
-            write_u64(&db, score.score);
-            write_u16(&db, score.comboMax);
-            write_u32(&db, score.modsLegacy);
+            write<u64>(&db, score.score);
+            write<u16>(&db, score.comboMax);
+            write<u32>(&db, score.modsLegacy);
 
             // custom
-            write_u16(&db, score.numSliderBreaks);
-            write_f32(&db, score.pp);
-            write_f32(&db, score.unstableRate);
-            write_f32(&db, score.hitErrorAvgMin);
-            write_f32(&db, score.hitErrorAvgMax);
-            write_f32(&db, score.starsTomTotal);
-            write_f32(&db, score.starsTomAim);
-            write_f32(&db, score.starsTomSpeed);
-            write_f32(&db, score.speedMultiplier);
-            write_f32(&db, score.CS);
-            write_f32(&db, score.AR);
-            write_f32(&db, score.OD);
-            write_f32(&db, score.HP);
+            write<u16>(&db, score.numSliderBreaks);
+            write<f32>(&db, score.pp);
+            write<f32>(&db, score.unstableRate);
+            write<f32>(&db, score.hitErrorAvgMin);
+            write<f32>(&db, score.hitErrorAvgMax);
+            write<f32>(&db, score.starsTomTotal);
+            write<f32>(&db, score.starsTomAim);
+            write<f32>(&db, score.starsTomSpeed);
+            write<f32>(&db, score.speedMultiplier);
+            write<f32>(&db, score.CS);
+            write<f32>(&db, score.AR);
+            write<f32>(&db, score.OD);
+            write<f32>(&db, score.HP);
 
             if(score.version > 20180722) {
-                write_u32(&db, score.maxPossibleCombo);
-                write_u32(&db, score.numHitObjects);
-                write_u32(&db, score.numCircles);
+                write<u32>(&db, score.maxPossibleCombo);
+                write<u32>(&db, score.numHitObjects);
+                write<u32>(&db, score.numCircles);
             }
 
             if(score.version >= 20240412) {
-                write_u32(&db, score.online_score_id);
+                write<u32>(&db, score.online_score_id);
                 write_string(&db, score.server.c_str());
             }
 

+ 1 - 1
src/App/Osu/Lobby.cpp

@@ -199,7 +199,7 @@ void Lobby::addRoom(Room* room) {
 void Lobby::joinRoom(u32 id, UString password) {
     Packet packet;
     packet.id = JOIN_ROOM;
-    write_u32(&packet, id);
+    write<u32>(&packet, id);
     write_string(&packet, password.toUtf8());
     send_packet(packet);
 

+ 2 - 4
src/App/Osu/MainMenu.cpp

@@ -273,7 +273,7 @@ MainMenu::MainMenu(Osu *osu) : OsuScreen(osu) {
 }
 
 MainMenu::~MainMenu() {
-    SAFE_DELETE(preloaded_beatmapset);
+    // SAFE_DELETE(preloaded_beatmapset); // double free??
     SAFE_DELETE(m_updateAvailableButton);
 
     anim->deleteExistingAnimation(&m_fUpdateButtonAnim);
@@ -1054,9 +1054,7 @@ void MainMenu::mouse_update(bool *propagate_clicks) {
     } else {
         if(music->isFinished()) {
             selectRandomBeatmap();
-        }
-
-        if(music->isPlaying()) {
+        } else if(music->isPlaying()) {
             m_pauseButton->setPaused(false);
 
             // NOTE: We set this every frame, because music loading isn't instant

+ 1 - 1
src/App/Osu/ModSelector.cpp

@@ -1188,7 +1188,7 @@ void ModSelector::resetModsUserInitiated() {
 
             Packet packet;
             packet.id = MATCH_CHANGE_MODS;
-            write_u32(&packet, bancho.room.slots[i].mods);
+            write<u32>(&packet, bancho.room.slots[i].mods);
             send_packet(packet);
 
             m_osu->m_room->updateLayout(m_osu->getScreenSize());

+ 8 - 8
src/App/Osu/RichPresence.cpp

@@ -61,12 +61,12 @@ void RichPresence::setBanchoStatus(Osu *osu, const char *info_text, Action actio
 
     Packet packet;
     packet.id = CHANGE_ACTION;
-    write_u8(&packet, action);
+    write<u8>(&packet, action);
     write_string(&packet, fancy_text);
     write_string(&packet, map_md5.hash);
-    write_u32(&packet, osu->m_modSelector->getModFlags());
-    write_u8(&packet, 0);  // osu!std
-    write_u32(&packet, map_id);
+    write<u32>(&packet, osu->m_modSelector->getModFlags());
+    write<u8>(&packet, 0);  // osu!std
+    write<u32>(&packet, map_id);
     send_packet(packet);
 }
 
@@ -85,12 +85,12 @@ void RichPresence::updateBanchoMods() {
 
     Packet packet;
     packet.id = CHANGE_ACTION;
-    write_u8(&packet, last_action);
+    write<u8>(&packet, last_action);
     write_string(&packet, last_status.toUtf8());
     write_string(&packet, map_md5.hash);
-    write_u32(&packet, bancho.osu->m_modSelector->getModFlags());
-    write_u8(&packet, 0);  // osu!std
-    write_u32(&packet, map_id);
+    write<u32>(&packet, bancho.osu->m_modSelector->getModFlags());
+    write<u8>(&packet, 0);  // osu!std
+    write<u32>(&packet, map_id);
     send_packet(packet);
 
     // Servers like akatsuki send different leaderboards based on what mods

+ 17 - 17
src/App/Osu/RoomScreen.cpp

@@ -798,7 +798,7 @@ void RoomScreen::onClientScoreChange(bool force) {
     Packet packet;
     packet.id = UPDATE_MATCH_SCORE;
 
-    write_u32(&packet, (i32)m_osu->getSelectedBeatmap()->getTime());
+    write<u32>(&packet, (i32)m_osu->getSelectedBeatmap()->getTime());
 
     u8 slot_id = 0;
     for(u8 i = 0; i < 16; i++) {
@@ -807,22 +807,22 @@ void RoomScreen::onClientScoreChange(bool force) {
             break;
         }
     }
-    write_u8(&packet, slot_id);
-
-    write_u16(&packet, (u16)m_osu->getScore()->getNum300s());
-    write_u16(&packet, (u16)m_osu->getScore()->getNum100s());
-    write_u16(&packet, (u16)m_osu->getScore()->getNum50s());
-    write_u16(&packet, (u16)m_osu->getScore()->getNum300gs());
-    write_u16(&packet, (u16)m_osu->getScore()->getNum100ks());
-    write_u16(&packet, (u16)m_osu->getScore()->getNumMisses());
-    write_u32(&packet, (i32)m_osu->getScore()->getScore());
-    write_u16(&packet, (u16)m_osu->getScore()->getCombo());
-    write_u16(&packet, (u16)m_osu->getScore()->getComboMax());
-    write_u8(&packet, m_osu->getScore()->getNumSliderBreaks() == 0 && m_osu->getScore()->getNumMisses() == 0 &&
-                          m_osu->getScore()->getNum50s() == 0 && m_osu->getScore()->getNum100s() == 0);
-    write_u8(&packet, m_osu->getSelectedBeatmap()->getHealth() * 200);
-    write_u8(&packet, 0);  // 4P, not supported
-    write_u8(&packet, m_osu->getModScorev2());
+    write<u8>(&packet, slot_id);
+
+    write<u16>(&packet, (u16)m_osu->getScore()->getNum300s());
+    write<u16>(&packet, (u16)m_osu->getScore()->getNum100s());
+    write<u16>(&packet, (u16)m_osu->getScore()->getNum50s());
+    write<u16>(&packet, (u16)m_osu->getScore()->getNum300gs());
+    write<u16>(&packet, (u16)m_osu->getScore()->getNum100ks());
+    write<u16>(&packet, (u16)m_osu->getScore()->getNumMisses());
+    write<u32>(&packet, (i32)m_osu->getScore()->getScore());
+    write<u16>(&packet, (u16)m_osu->getScore()->getCombo());
+    write<u16>(&packet, (u16)m_osu->getScore()->getComboMax());
+    write<u8>(&packet, m_osu->getScore()->getNumSliderBreaks() == 0 && m_osu->getScore()->getNumMisses() == 0 &&
+                           m_osu->getScore()->getNum50s() == 0 && m_osu->getScore()->getNum100s() == 0);
+    write<u8>(&packet, m_osu->getSelectedBeatmap()->getHealth() * 200);
+    write<u8>(&packet, 0);  // 4P, not supported
+    write<u8>(&packet, m_osu->getModScorev2());
     send_packet(packet);
 
     last_packet_tms = time(NULL);

+ 1 - 1
src/App/Osu/UIModSelectorModButton.cpp

@@ -138,7 +138,7 @@ void UIModSelectorModButton::onClicked() {
             debugLog("Sending mod change to server.\n");
             Packet packet;
             packet.id = MATCH_CHANGE_MODS;
-            write_u32(&packet, bancho.room.slots[i].mods);
+            write<u32>(&packet, bancho.room.slots[i].mods);
             send_packet(packet);
 
             m_osu->m_room->on_room_updated(bancho.room);

+ 4 - 4
src/App/Osu/UIUserContextMenu.cpp

@@ -96,12 +96,12 @@ void UIUserContextMenuScreen::on_action(UString text, int user_action) {
     if(user_action == UA_TRANSFER_HOST) {
         Packet packet;
         packet.id = TRANSFER_HOST;
-        write_u32(&packet, slot_number);
+        write<u32>(&packet, slot_number);
         send_packet(packet);
     } else if(user_action == KICK) {
         Packet packet;
         packet.id = MATCH_LOCK;
-        write_u32(&packet, slot_number);
+        write<u32>(&packet, slot_number);
         send_packet(packet);  // kick by locking the slot
         send_packet(packet);  // unlock the slot
     } else if(user_action == START_CHAT) {
@@ -113,13 +113,13 @@ void UIUserContextMenuScreen::on_action(UString text, int user_action) {
     } else if(user_action == UA_ADD_FRIEND) {
         Packet packet;
         packet.id = FRIEND_ADD;
-        write_u32(&packet, m_user_id);
+        write<u32>(&packet, m_user_id);
         send_packet(packet);
         friends.push_back(m_user_id);
     } else if(user_action == UA_REMOVE_FRIEND) {
         Packet packet;
         packet.id = FRIEND_REMOVE;
-        write_u32(&packet, m_user_id);
+        write<u32>(&packet, m_user_id);
         send_packet(packet);
 
         auto it = std::find(friends.begin(), friends.end(), m_user_id);

+ 4 - 0
src/Engine/Engine.cpp

@@ -105,6 +105,9 @@ Console *Engine::m_console = NULL;
 ConsoleBox *Engine::m_consoleBox = NULL;
 
 Engine::Engine(Environment *environment, const char *args) {
+    // XXX: run curl_global_cleanup() after waiting for network threads to terminate
+    curl_global_init(CURL_GLOBAL_DEFAULT);
+
     engine = this;
     m_environment = environment;
     env = environment;
@@ -527,6 +530,7 @@ void Engine::onShutdown() {
     if(m_bBlackout || (m_app != NULL && !m_app->onShutdown())) return;
 
     m_bBlackout = true;
+    m_sound->shutdown();
     m_environment->shutdown();
 }
 

+ 13 - 25
src/Engine/SoundEngine.cpp

@@ -319,22 +319,7 @@ void SoundEngine::updateOutputDevices(bool printInfo) {
 bool SoundEngine::initializeOutputDevice(OUTPUT_DEVICE device) {
     debugLog("SoundEngine: initializeOutputDevice( %s ) ...\n", device.name.toUtf8());
 
-    if(m_currentOutputDevice.driver == OutputDriver::BASS) {
-        BASS_SetDevice(m_currentOutputDevice.id);
-        BASS_Free();
-        BASS_SetDevice(0);
-    }
-
-#ifdef _WIN32
-    if(m_currentOutputDevice.driver == OutputDriver::BASS_ASIO) {
-        BASS_ASIO_Free();
-    } else if(m_currentOutputDevice.driver == OutputDriver::BASS_WASAPI) {
-        BASS_WASAPI_Free();
-    }
-#endif
-
-    g_bassOutputMixer = 0;
-    BASS_Free();  // free "No sound" device
+    shutdown();
 
     if(device.driver == OutputDriver::NONE || (device.driver == OutputDriver::BASS && device.id == 0)) {
         m_bReady = true;
@@ -504,23 +489,26 @@ bool SoundEngine::initializeOutputDevice(OUTPUT_DEVICE device) {
     return true;
 }
 
-SoundEngine::~SoundEngine() {
+void SoundEngine::restart() { setOutputDevice(m_currentOutputDevice); }
+
+void SoundEngine::shutdown() {
     if(m_currentOutputDevice.driver == OutputDriver::BASS) {
+        BASS_SetDevice(m_currentOutputDevice.id);
         BASS_Free();
-    } else if(m_currentOutputDevice.driver == OutputDriver::BASS_ASIO) {
+        BASS_SetDevice(0);
+    }
+
 #ifdef _WIN32
+    if(m_currentOutputDevice.driver == OutputDriver::BASS_ASIO) {
         BASS_ASIO_Free();
-#endif
-        BASS_Free();
     } else if(m_currentOutputDevice.driver == OutputDriver::BASS_WASAPI) {
-#ifdef _WIN32
         BASS_WASAPI_Free();
-#endif
-        BASS_Free();
     }
-}
+#endif
 
-void SoundEngine::restart() { setOutputDevice(m_currentOutputDevice); }
+    g_bassOutputMixer = 0;
+    BASS_Free();  // free "No sound" device
+}
 
 void SoundEngine::update() {}
 

+ 1 - 1
src/Engine/SoundEngine.h

@@ -29,9 +29,9 @@ struct OUTPUT_DEVICE {
 class SoundEngine {
    public:
     SoundEngine();
-    ~SoundEngine();
 
     void restart();
+    void shutdown();
 
     void update();