From 0654addb98c1a55ac1d386883375d1ecb37ce958 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?N=C3=ADcolas=20Wildner?= Date: Sun, 5 Jul 2020 22:52:42 -0300 Subject: [PATCH 01/85] Updating pt_BR translation --- assets/voxygen/i18n/pt_BR.ron | 88 ++++++++++++++++++++++++++++++++--- 1 file changed, 82 insertions(+), 6 deletions(-) diff --git a/assets/voxygen/i18n/pt_BR.ron b/assets/voxygen/i18n/pt_BR.ron index f6b5b1f694..3537bb6d5b 100644 --- a/assets/voxygen/i18n/pt_BR.ron +++ b/assets/voxygen/i18n/pt_BR.ron @@ -123,12 +123,13 @@ https://account.veloren.net."#, "main.login.failed_sending_request": "Requisição ao servidor de autenticação falhou", "main.login.invalid_character": "O personagem selecionado é inválido", "main.login.client_crashed": "Cliente abortou", + "main.login.not_on_whitelist": "Você precisa ser permitido por um Admin para ingressar", /// End Main screen section /// Start HUD Section - "hud.do_not_show_on_startup": "Nâo mostrar no início", + "hud.do_not_show_on_startup": "Não mostrar no início", "hud.show_tips": "Mostrar dicas", "hud.quests": "Missões", "hud.you_died": "Você Morreu", @@ -139,6 +140,18 @@ https://account.veloren.net."#, "hud.press_key_to_toggle_keybindings_fmt": "Pressione {key} para mostrar/ocultar teclas mapeadas", "hud.press_key_to_toggle_debug_info_fmt": "Pressione {key} para mostrar/ocultar informações de depuração", + // Chat outputs + "hud.chat.online_msg": "[{name}] está online.", + "hud.chat.offline_msg": "{name} está offline.", + "hud.chat.loot_msg": "Você pegou [{item}]", + "hud.chat.loot_fail": "Seu Inventário está cheio!", + "hud.chat.goodbye": "Tchau!", + "hud.chat.connection_lost": "Connection lost. Kicking in {time} seconds.", + + // SCT outputs + "hud.sct.experience": "{amount} Exp", + "hud.sct.block": "BLOQUEADO", + // Respawn message "hud.press_key_to_respawn": r#"Pressione {key} para renascer na última fogueira visitada."#, @@ -175,6 +188,19 @@ Quer liberar o cursor do mouse para fechar esta janela? Pressione TAB! Aproveite a sua estadia no Mundo de Veloren."#, +"hud.temp_quest_headline": r#"Por favor, nos ajude Viajante!"#, +"hud.temp_quest_text": r#"As Cavernas estão repletas de cultistas maus +que emergiram para nossos pacíficos vilarejos! + + +Consiga companheiros, junte comida +e derrote seus líderes vis e acólitos. + + +Talvez você até consiga obter um dos seus +itens magicamente modificados?"#, + + // Inventory "hud.bag.inventory": "Inventário de {playername}", @@ -223,12 +249,15 @@ Aproveite a sua estadia no Mundo de Veloren."#, "hud.settings.cumulated_damage": "Dano Acumulado", "hud.settings.incoming_damage": "Dano Recebido", "hud.settings.cumulated_incoming_damage": "Dano Recebido Acumulado", - "hud.settings.speech_bubble_dark_mode": "Balão de fala no modo escuro", + "hud.settings.speech_bubble": "Balão de Fala", + "hud.settings.speech_bubble_dark_mode": "Balão de Fala no modo escuro", + "hud.settings.speech_bubble_icon": "Ícone do Balão de Fala", "hud.settings.energybar_numbers": "Números da Barra de energia", "hud.settings.values": "Valores", "hud.settings.percentages": "Porgentavens", "hud.settings.chat": "Chat", "hud.settings.background_transparency": "Transparência de Fundo", + "hud.settings.chat_character_name": "Nomes de Personagem no Chat", "hud.settings.pan_sensitivity": "Sensibilidade de Rotação", "hud.settings.zoom_sensitivity": "Sensibilidade do Zoom", @@ -236,6 +265,8 @@ Aproveite a sua estadia no Mundo de Veloren."#, "hud.settings.invert_mouse_y_axis": "Inverter eixo Y do Mouse", "hud.settings.enable_mouse_smoothing": "Suavização da Câmera", "hud.settings.free_look_behavior": "Comportamento de Câmera Livre", + "hud.settings.auto_walk_behavior": "Comportamento do caminhar automático", + "hud.settings.stop_auto_walk_on_input": "Parar caminhar automático em caso de movimento", "hud.settings.view_distance": "Alcance de visão", "hud.settings.sprites_view_distance": "Distância de visão dos Sprites", @@ -320,6 +351,9 @@ Aproveite a sua estadia no Mundo de Veloren."#, "gameinput.togglewield": "Alternar Empunhadura", "gameinput.interact": "Interagir", "gameinput.freelook": "Câmera Livre", + "gameinput.autowalk": "Caminhar Automático", + "gameinput.dance": "Dançar", + /// End GameInput section @@ -340,7 +374,8 @@ Aproveite a sua estadia no Mundo de Veloren."#, "char_selection.uncanny_valley": "Vale Misterioso", "char_selection.plains_of_uncertainty": "Planícies da Incerteza", "char_selection.beard": "Barba", - "char_selection.hair_style": "Estilo do cabelo", + "char_selection.hair_style": "Estilo do Cabelo", + "char_selection.hair_color": "Cor do Cabelo", "char_selection.eye_color": "Cor de olho", "char_selection.skin": "Pele", "char_selection.eyeshape": "Detalhe do olho", @@ -359,8 +394,6 @@ Aptidão Força de Vontade "#, - - /// Start character window section @@ -407,7 +440,50 @@ Força de Vontade "Estão atrás de mim!", "Ajuda! Ajuda! Estou sendo repreendido", "Ah, agora vemos a violência inerente ao sistema.", - "Só um arranhão!" + "Só um arranhão!", + "Pare com isso!", + "O que eu fiz para você?!", + "Por favor, pare de me atacar!", + "Hey! Cuida pra onde você aponta essa coisa!", + "Desgraçado hediondo, vou acabar com você!", + "Pare com isso! Vá embora!", + "Você está me deixando louco!", + "Ow! Quem você pensa que é?!", + "Arrancarei sua cabeça por isso!", + "Pare, por favor! Não levo nada de valor!", + "Vou mandar meu irmão em você, ele é maior que eu!", + "Nãooo, Vou contar pra minha mãe!", + "Te amaldiçoo!", + "Por favor não faça isso.", + "Isso não foi muito legal!", + "Sua arma funciona, pode guardar ela agora!", + "Me poupe!", + "Por favor, tenho uma família!", + "Sou muito jovem para morrer!", + "Podemos conversar sobre isso?", + "Violência nunca é a resposta!", + "Hoje foi um péssimo dia...", + "Ei, isso dói!", + "Eek!", + "Que rude!", + "Pare, eu imploro!", + "Que você adoeça!", + "Isso não é engraçado.", + "Como ousa?!", + "Você pagará por isso!", + "Continue assim e irá se arrepender!", + "Não me faça te machucar!", + "Deve haver algum engano!", + "Não precisa fazer isso!", + "Morre, Diabo!", + "Isso Dói!", + "Porque você faria isso?", + "Pelos espíritos, Pare!", + "Você deve ter me confundido com alguém!", + "Eu não mereço isso!", + "Por favor, não faça isso novamente.", + "Guardas, joguem este monstro no lago!", + "Vou mandar meu tarrasque em você!", ], } ) From 9f7f9f96f9576fe4cb92471a0aa570545126bda5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?N=C3=ADcolas=20Wildner?= Date: Mon, 6 Jul 2020 01:57:34 +0000 Subject: [PATCH 02/85] Update pt_BR.ron --- assets/voxygen/i18n/pt_BR.ron | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/voxygen/i18n/pt_BR.ron b/assets/voxygen/i18n/pt_BR.ron index 3537bb6d5b..453916c477 100644 --- a/assets/voxygen/i18n/pt_BR.ron +++ b/assets/voxygen/i18n/pt_BR.ron @@ -146,7 +146,7 @@ https://account.veloren.net."#, "hud.chat.loot_msg": "Você pegou [{item}]", "hud.chat.loot_fail": "Seu Inventário está cheio!", "hud.chat.goodbye": "Tchau!", - "hud.chat.connection_lost": "Connection lost. Kicking in {time} seconds.", + "hud.chat.connection_lost": "Conexão perdida. Chutando em {time} segundos.", // SCT outputs "hud.sct.experience": "{amount} Exp", From 2ce92b88a9ff55444b64423a4f18e98437412476 Mon Sep 17 00:00:00 2001 From: Jack Rubino Date: Mon, 6 Jul 2020 14:27:55 +0000 Subject: [PATCH 03/85] Update it_IT.ron --- assets/voxygen/i18n/it_IT.ron | 380 ++++++++++++++++++++-------------- 1 file changed, 219 insertions(+), 161 deletions(-) diff --git a/assets/voxygen/i18n/it_IT.ron b/assets/voxygen/i18n/it_IT.ron index 657821da47..bcefb59a0e 100644 --- a/assets/voxygen/i18n/it_IT.ron +++ b/assets/voxygen/i18n/it_IT.ron @@ -65,6 +65,7 @@ VoxygenLocalization( "common.back": "Indietro", "common.create": "Crea", "common.okay": "Ok", + "common.accept": "Accetta", "common.disclaimer": "Disclaimer", "common.cancel": "Cancella", "common.none": "Nessuno", @@ -139,11 +140,6 @@ Se ti piace ciò che vedi, sei il benvenuto ad unirti ai team di sviluppo e arti -Come loro, stiamo cercando di costruire una nicchia. Il gioco non è un clone, e il suo sviluppo divergerà dai giochi esistenti in futuro. - - - - Grazie per aver dedicato del tempo a leggere questo avviso, speriamo che ti divertirai col gioco! @@ -176,7 +172,9 @@ https://account.veloren.net."#, "main.login.already_logged_in": "Hai già effettuato l'accesso al server", "main.login.network_error": "Errore di rete", "main.login.failed_sending_request": "Richiesta ai server di autenticazione fallita", + "main.login.invalid_character": "Il personaggio selezionato è invalido", "main.login.client_crashed": "Il client si è arrestato", + "main.login.not_on_whitelist": "Hai bisogno di un permesso di accesso da un admin per entrare", @@ -200,22 +198,31 @@ https://account.veloren.net."#, "hud.show_tips": "Mostra consigli", "hud.quests": "Missioni", "hud.you_died": "Sei Morto", + "hud.waypoint_saved": "Waypoint Salvato", "hud.press_key_to_show_keybindings_fmt": "Premi {key} per mostrare le scorciatoie da tastiera", "hud.press_key_to_show_debug_info_fmt": "Premi {key} per mostrare le informazioni di debug", "hud.press_key_to_toggle_keybindings_fmt": "Premi {key} per attivare/disattivare le scorciatoie da tastiera", "hud.press_key_to_toggle_debug_info_fmt": "Premi {key} per attivare/disattivare le informazioni di debug", + // Chat outputs + "hud.chat.online_msg": "[{name}] è ora online.", + "hud.chat.offline_msg": "{name} è andato offline.", + "hud.chat.loot_msg": "Hai raccolto [{item}]", + "hud.chat.loot_fail": "Il tuo inventario è pieno!", + "hud.chat.goodbye": "Addio!", + "hud.chat.connection_lost": "Connessione persa. Espulsione in {time} secondi.", + + // SCT outputs + "hud.sct.experience": "{amount} Esp", + "hud.sct.block": "PARATO", + + // Respawn message - "hud.press_key_to_respawn": r#"Premi {key} per rinascere al tuo Waypoint. - - - - -Premi Invio, scrivi /waypoint e conferma per impostarlo qui."#, + "hud.press_key_to_respawn": r#"Premi {key} per rinascere all'ultimo falò visitato."#, @@ -229,16 +236,6 @@ Premi Invio, scrivi /waypoint e conferma per impostarlo qui."#, Alcuni consigli prima di cominciare: -MOLTO IMPORTANTE: Per impostare il tuo punto di rinascita scrivi /waypoint - - -nella chat. - - -Ciò può essere fatto anche se sei già morto! - - - Premi F1 per vedere i comandi chiave disponibili. @@ -268,7 +265,7 @@ Gettali via cliccandoci una volta sopra e una volta fuori dall’inventario. Le notti possono essere molto buie in Veloren. -Accendi la tua lanterna scrivendo /lantern nella chat. +Accendi la tua lanterna premendo 'G' @@ -280,19 +277,47 @@ Vuoi sbloccare il cursore per chiudere questa finestra? Premi TAB! Goditi il tuo soggiorno nel Mondo di Veloren."#, -"hud.temp_quest_headline": r#"Please, help us Traveller!"#, -"hud.temp_quest_text": r#"Dungeons filled with evil cultists -have emerged all around our peaceful towns! +"hud.temp_quest_headline": r#"Perfavore, aiutaci avventuriero!"#, +"hud.temp_quest_text": r#"Dungeon pieni di cultisti malvagi +sono emersi tutto intorno alle nostre pacifiche cittadine! -Gather some company, stack up on food -and defeat their vile leaders and acolytes. +Raduna un equipaggio, rifornisciti di viveri +e sconfiggi i loro vili leader e accoliti. -Maybe you can even obtain one of their -magically infused items?"#, +Forse potresti persino ottenere uno dei loro +oggetti infusi di magia?"#, + + // Inventory + "hud.bag.inventory": "Inventario di {playername}", + "hud.bag.stats_title": "Statistiche di {playername}", + "hud.bag.exp": "Esperienza", + "hud.bag.armor": "Armatura", + "hud.bag.stats": "Statistiche", + "hud.bag.head": "Testa", + "hud.bag.neck": "Collo", + "hud.bag.tabard": "Cotta di maglia", + "hud.bag.shoulders": "Spalle", + "hud.bag.chest": "Torace", + "hud.bag.hands": "Mani", + "hud.bag.lantern": "Lanterna", + "hud.bag.belt": "Cintura", + "hud.bag.ring": "Anello", + "hud.bag.back": "Schiena", + "hud.bag.legs": "Gambe", + "hud.bag.feet": "Piedi", + "hud.bag.mainhand": "Mano Principale", + "hud.bag.offhand": "Mano Secondaria", + + + // Map and Questlog + "hud.map.map_title": "Mappa", + "hud.map.qlog_title": "Missioni", + + // Settings "hud.settings.general": "Generale", "hud.settings.none": "Nessuno", "hud.settings.press_behavior.toggle": "Attiva/Disattiva", @@ -313,12 +338,15 @@ magically infused items?"#, "hud.settings.cumulated_damage": "Danno Nemico (Cumulativo)", "hud.settings.incoming_damage": "Danno Giocatore (Singolo)", "hud.settings.cumulated_incoming_damage": "Danno Giocatore (Cumulativo)", + "hud.settings.speech_bubble": "Fumetto", + "hud.settings.speech_bubble_dark_Spmode": "Fumetto Modalità Scura", + "hud.settings.speech_bubble_icon": "Icona Fumetto", "hud.settings.energybar_numbers": "Numeri Barra dell’Energia", "hud.settings.values": "Valori", "hud.settings.percentages": "Percentuali", "hud.settings.chat": "Chat", "hud.settings.background_transparency": "Trasparenza dello Sfondo", - "hud.settings.none": "Nessuno", + "hud.settings.chat_character_name": "Nome dei personaggi in chat", @@ -327,12 +355,18 @@ magically infused items?"#, "hud.settings.zoom_sensitivity": "Sensibilità Zoom", "hud.settings.invert_scroll_zoom": "Zoom Invertito", "hud.settings.invert_mouse_y_axis": "Asse Y del Mouse Invertito", + "hud.settings.enable_mouse_smoothing": "Camera Smoothing", "hud.settings.free_look_behavior": "Comportamento Visuale Libera", + "hud.settings.auto_walk_behavior": "Comportamento Camminata Automatica", + "hud.settings.stop_auto_walk_on_input": "Interrompere Camminata Automatica dopo movimento", + "hud.settings.view_distance": "Distanza Oggetto", + "hud.settings.sprites_view_distance": "Distanza Sprite", + "hud.settings.figures_view_distance": "Distanza Entità", "hud.settings.maximum_fps": "FPS Massimi", "hud.settings.fov": "Campo Visivo (gradi)", "hud.settings.gamma": "Gamma", @@ -352,145 +386,98 @@ magically infused items?"#, "hud.settings.sound_effect_volume": "Volume Effetti Sonori", "hud.settings.audio_device": "Dispositivo Audio", - - - - // Control list - "hud.settings.control_names": r#"Cursore Libero -Attiva/Disattiva Finestra di Aiuto -Attiva/Disattiva Interfaccia -Attiva/Disattiva FPS e Informazioni di Debug -Scatta Screenshot -Attiva/Disattiva Nomi -Attiva/Disattiva Schermo Intero - - -Movimento in Avanti -Movimento a Sinistra -Movimento a Destra -Movimento all’Indietro - -Salto - -Aliante - -Schivata - -Rotolata - -Scalata - -Discesa - -Camminata Automatica - -Riporre/Sfoderare Armi - -Mettere/Rimuovere Elmo - -Sedersi - -Cavalcatura - -Interagire - - -Attacco Base -Attacco Secondario/Parata/Mira - - -Slot 1 Barra delle Abilità -Slot 2 Barra delle Abilità -Slot 3 Barra delle Abilità -Slot 4 Barra delle Abilità -Slot 5 Barra delle Abilità -Slot 6 Barra delle Abilità -Slot 7 Barra delle Abilità -Slot 8 Barra delle Abilità -Slot 9 Barra delle Abilità -Slot 10 Barra delle Abilità - - -Menù di Pausa -Impostazioni -Social -Mappa -Libro degli Incantesimi -Personaggio -Diario delle Missioni -Borsa - - - -Invia Messaggio nella Chat -Scorrimento della Chat - -Camera Libera - - - -Comandi della Chat: - -/alias [Name] - Cambia il tuo Nome nella Chat -/tp [Name] - Ti teleporta da un altro giocatore -/jump - Devia la tua posizione -/goto - Ti teleporta in una posizione -/kill - Suicidati -/pig - Fai apparire un maiale PNG -/wolf - Fai apparire un lupo PNG -/help - Mostra comandi della Chat"#, - - - - - "hud.social": "Social", + "hud.settings.awaitingkey": "Premi un tasto...", + + "hud.social": "Sociale", "hud.social.online": "Online", "hud.social.friends": "Amici", "hud.social.not_yet_available": "Non ancora disponibile", "hud.social.faction": "Fazione", "hud.social.play_online_fmt": "{nb_player} giocatore/i online", - - - - - "hud.spell": "Incantesimo", - "hud.free_look_indicator": "Visuale Libera Attiva", + "hud.spell": "Incantesimi", + + "hud.free_look_indicator": "Visuale libera attiva", + "hud.auto_walk_indicator": "Camminata automatica attiva", + /// End HUD section + + /// Start GameInput section - - - - - - - + "gameinput.primary": "Attacco Base", + "gameinput.secondary": "Attacco Secondario/Parata/Mira", + "gameinput.slot1": "Barra delle Abilità 1", + "gameinput.slot2": "Barra delle Abilità 2", + "gameinput.slot3": "Barra delle Abilità 3", + "gameinput.slot4": "Barra delle Abilità 4", + "gameinput.slot5": "Barra delle Abilità 5", + "gameinput.slot6": "Barra delle Abilità 6", + "gameinput.slot7": "Barra delle Abilità 7", + "gameinput.slot8": "Barra delle Abilità 8", + "gameinput.slot9": "Barra delle Abilità 9", + "gameinput.slot10": "Barra delle Abilità 10", + "gameinput.swaploadout": "Scambia Equipaggiamento", + "gameinput.togglecursor": "Attiva/Disattiva Cursore", + "gameinput.help": "Attiva/Disattiva Finestra d'Aiuto", + "gameinput.toggleinterface": "Attiva/Disattiva Interfaccia", + "gameinput.toggledebug": "Attiva/Disattiva FPS e Informazioni di Debug", + "gameinput.screenshot": "Scatta uno Screenshot", + "gameinput.toggleingameui": "Attiva/Disattiva Nomi", + "gameinput.fullscreen": "Attiva/Disattiva Fullscreen", + "gameinput.moveforward": "Movimento in Avanti", + "gameinput.moveleft": "Movimento a Sinistra", + "gameinput.moveright": "Movimento a Destra", + "gameinput.moveback": "Movimento Indietro", + "gameinput.jump": "Salto", + "gameinput.glide": "Aliante", + "gameinput.roll": "Rotolata", + "gameinput.climb": "Scalata", + "gameinput.climbdown": "Scalata in Giù", + "gameinput.wallleap": "Salto al Muro", + "gameinput.togglelantern": "Attiva/Disattiva Lanterna", + "gameinput.mount": "Cavalcatura", + "gameinput.enter": "Invio", + "gameinput.command": "Comando", + "gameinput.escape": "Escape", + "gameinput.map": "Mappa", + "gameinput.bag": "Borsa", + "gameinput.social": "Sociale", + "gameinput.sit": "Sedersi", + "gameinput.spellbook": "Incantesimi", + "gameinput.settings": "Opzioni", + "gameinput.respawn": "Rinascita", + "gameinput.charge": "Carica", + "gameinput.togglewield": "Sfodera/Rinfodera Arma", + "gameinput.interact": "Interagire", + "gameinput.freelook": "Visuale Libera", + "gameinput.autowalk": "Camminata Automatica", + "gameinput.dance": "Danza", + + /// End GameInput section + /// Start character selection section + "char_selection.loading_characters": "Caricamento Personaggio...", "char_selection.delete_permanently": "Eliminare permanente questo Personaggio?", + "char_selection.deleting_character": "Cancellazione Personaggio...", "char_selection.change_server": "Cambia Server", "char_selection.enter_world": "Unisciti al Mondo", "char_selection.logout": "Disconnettiti", "char_selection.create_new_charater": "Crea un nuovo Personaggio", + "char_selection.creating_character": "Creazione Personaggio...", "char_selection.character_creation": "Creazione Personaggio", - - - "char_selection.human_default": "Umano Predefinito", "char_selection.level_fmt": "Livello {level_nb}", - "char_selection.uncanny_valley": "Valle Perturbante", + "char_selection.uncanny_valley": "Regione Selvaggia", "char_selection.plains_of_uncertainty": "Pianure dell'Incertezza", "char_selection.beard": "Barba", "char_selection.hair_style": "Stile Capelli", "char_selection.hair_color": "Colore Capelli", - "char_selection.chest_color": "Colore Torace", "char_selection.eye_color": "Colore Occhi", "char_selection.skin": "Pelle", - "char_selection.eyebrows": "Sopracciglia", + "char_selection.eyeshape": "Dettagli Occhi", "char_selection.accessories": "Accessori", - - - + "char_selection.create_info_name": "Il tuo personaggio necessita di un nome!", /// End chracter selection section @@ -516,26 +503,97 @@ Vitalità Volontà "#, + /// End character window section - - - - - - - /// Start character window section - - - - - - /// Start Escape Menu Section - "esc_menu.logout": "Disconnettiti", - "esc_menu.quit_game": "Esci dal Gioco", - /// End Escape Menu Section +/// Start Escape Menu Section + "esc_menu.logout": "Disconnettiti", + "esc_menu.quit_game": "Esci dal Gioco", + /// End Escape Menu Section }, - vector_map: { + vector_map: { + "npc.speech.villager_under_attack": [ + "Aiuto, sono sotto attacco!", + "Aiuto! Sono sotto attacco!", + "Ouch! Sono sotto attacco!", + "Ouch! Sono sotto attacco! Aiuto!", + "Aiutatemi! Sono sotto attacco!", + "Sono sotto attacco! Aiuto!", + "Sono sotto attacco! Aiutatemi!", + "Aiuto!", + "Aiuto! Aiuto!", + "Aiuto! Aiuto! Aiuto!", + "Sono sotto attacco!", + "AAAHHH! Sono sotto attacco!", + "AAAHHH! Sono sotto attacco! Aiuto!", + "Aiuto! Siamo sotto attacco!", + "Aiuto! Assassino!", + "Aiuto! C'è un assassino in circolazione!", + "Aiuto! Stanno cercando di uccidermi!", + "Guardie, sono sotto attacco!", + "Guardie! Sono sotto attacco!", + "Sono sotto attacco! Guardie!", + "Aiuto! Guardie! Sono sotto attacco!", + "Guardie! Venite presto!", + "Guardie! Guardie!", + "Guardie! C'è un furfante che mi sta attaccando!", + "Guardie, ammazzate questa disonesta canaglia!", + "Guardie! C'è un assassino!", + "Guardie! Aiutatemi!", + "Non la farai franca dopo questa! Guardie!", + "Mostro!", + "Aiutatemi!", + "Aiuto! Per favore!", + "Ouch! Guardie! Aiuto!", + "Stanno venendo per me!", + "Aiuto! Aiuto! Sto venendo represso!", + "Ah, ora vediamo la violenza insita nel sistema.", + "Non è altro che un graffio!", + "Smettila!", + "Che cosa ti ho mai fatto?!", + "Per favore smettila di attaccarmi!", + "Hey! Attento a dove punti quella cosa!", + "Odioso miserabile, vattene via!", + "Smettila! Vai via!", + "Adesso mi stai facendo arrabbiare!", + "Oi! Chi pensi di essere?!", + "Avrò la tua testa per ciò!", + "Fermati, ti prego! Non ho con me nulla di valore!", + "Ti metterò contro mio fratello, è più grande di me!", + "Nooo, glielo dico alla mamma!", + "Che tu sia maledetto!", + "Ti prego di non farlo.", + "Non è stato molto carino!", + "La tua arma funziona, puoi metterla via ora!", + "Risparmiami!", + "Ti prego, ho una famiglia!", + "Sono troppo giovane per morire!", + "Possiamo parlarne?", + "La violenza non è mai la risposta!", + "Oggi sta per diventare un giorno molto brutto...", + "Hey, ha fatto male!", + "Eek!", + "Quanto rude!", + "Fermati, te ne prego!", + "Che ti prenda il vaiolo!", + "Non è divertente.", + "Come ti permetti?!", + "La pagherai per questo!", + "Continua così e te ne pentirai!", + "Non costringermi a farti del male!", + "Ci deve essere un malinteso!", + "Non hai bisogno di fare così!", + "Vattene via, mostro!", + "Quello ha fatto veramente male!", + "Perché mai faresti una cosa del genere?", + "Per gli spiriti, fermati!", + "Mi devi aver confuso con qualcun altro!", + "Non mi merito tutto ciò!", + "Per favore non lo rifare.", + "Guardie, gettate questo mostro nel lago!", + "Sguinzaglierò il mio tarrasque su di te!", + ], } + ) From 4232148b83358b4eb994e7cf7788684f1c4115a5 Mon Sep 17 00:00:00 2001 From: Giovanni Spera Date: Tue, 7 Jul 2020 20:56:44 +0200 Subject: [PATCH 04/85] Stopping auto-walk after death: Fixes #607 --- voxygen/src/session.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/voxygen/src/session.rs b/voxygen/src/session.rs index ea83cf918c..3dfaa7973b 100644 --- a/voxygen/src/session.rs +++ b/voxygen/src/session.rs @@ -326,6 +326,7 @@ impl PlayState for SessionState { Event::InputUpdate(GameInput::Respawn, state) if state != self.key_state.respawn => { + stop_auto_walk(&mut auto_walk, &mut self.key_state, &mut self.hud); self.key_state.respawn = state; if state { self.client.borrow_mut().respawn(); From 1f1d9c5625b3907a8a23a55cfc023b0c02facc6a Mon Sep 17 00:00:00 2001 From: Treeco <5021038-Treeco@users.noreply.gitlab.com> Date: Thu, 9 Jul 2020 15:46:00 +0100 Subject: [PATCH 05/85] Reset rather than accumulate velocity when stuck or in unloaded chunks --- common/src/sys/phys.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/common/src/sys/phys.rs b/common/src/sys/phys.rs index d57ef92df1..560adeb196 100644 --- a/common/src/sys/phys.rs +++ b/common/src/sys/phys.rs @@ -134,6 +134,7 @@ impl<'a> System<'a> for Sys { let dt_lerp = 0.2; (vel.0 * dt_lerp + old_vel.0 * (1.0 - dt_lerp)) * dt.0 } else { + vel.0 = Vec3::zero(); Vec3::zero() }; @@ -319,6 +320,7 @@ impl<'a> System<'a> for Sys { } if attempts == MAX_ATTEMPTS { + vel.0 = Vec3::zero(); pos.0 = old_pos; break; } From 23c774c8daad0549f42049248a219c79f1e67a38 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Tue, 7 Jul 2020 12:42:05 +0100 Subject: [PATCH 06/85] Fixed missed VD update bug --- server/src/sys/message.rs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/server/src/sys/message.rs b/server/src/sys/message.rs index 3ea09a0003..f346f1fab0 100644 --- a/server/src/sys/message.rs +++ b/server/src/sys/message.rs @@ -146,20 +146,20 @@ impl Sys { }, ClientMsg::SetViewDistance(view_distance) => { if let ClientState::Character { .. } = client.client_state { + players.get_mut(entity).map(|player| { + player.view_distance = Some( + settings + .max_view_distance + .map(|max| view_distance.min(max)) + .unwrap_or(view_distance), + ) + }); + if settings .max_view_distance - .map(|max| view_distance <= max) - .unwrap_or(true) + .map(|max| view_distance > max) + .unwrap_or(false) { - players.get_mut(entity).map(|player| { - player.view_distance = Some( - settings - .max_view_distance - .map(|max| view_distance.min(max)) - .unwrap_or(view_distance), - ) - }); - } else { client.notify(ServerMsg::SetViewDistance( settings.max_view_distance.unwrap_or(0), )); From 47e413c530d226ab7467c5a82f167b610740d3fa Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Tue, 7 Jul 2020 18:23:01 +0100 Subject: [PATCH 07/85] Improved pathfinding tolerance and reliability --- common/src/path.rs | 210 ++++++++++++++++++++++++++-------------- common/src/sys/agent.rs | 2 +- 2 files changed, 136 insertions(+), 76 deletions(-) diff --git a/common/src/path.rs b/common/src/path.rs index 8388edb36c..b51bea7cb3 100644 --- a/common/src/path.rs +++ b/common/src/path.rs @@ -75,86 +75,124 @@ impl Route { where V: BaseVol + ReadVol, { - let next0 = self - .next(0) - .unwrap_or_else(|| pos.map(|e| e.floor() as i32)); - let next1 = self.next(1).unwrap_or(next0); - if vol.get(next0).map(|b| b.is_solid()).unwrap_or(false) { - None - } else { - let next_tgt = next0.map(|e| e as f32) + Vec3::new(0.5, 0.5, 0.0); - if pos.xy().distance_squared(next_tgt.xy()) < traversal_tolerance.powf(2.0) - && next_tgt.z - pos.z < 0.2 - && next_tgt.z - pos.z > -2.2 + let (next0, next1, next_tgt) = loop { + let next0 = self + .next(0) + .unwrap_or_else(|| pos.map(|e| e.floor() as i32)); + + // Stop using obstructed paths + if vol.get(next0).map(|b| b.is_solid()).unwrap_or(false) { + return None; + } + + let next1 = self.next(1).unwrap_or(next0); + let next0_tgt = next0.map(|e| e as f32) + Vec3::new(0.5, 0.5, 0.0); + let next1_tgt = next1.map(|e| e as f32) + Vec3::new(0.5, 0.5, 0.0); + + // We might be able to skip a node in some cases to avoid doubling-back + let closest_tgt = if next0_tgt.distance_squared(pos) < next1_tgt.distance_squared(pos) { + next0_tgt + } else { + next1_tgt + }; + + // Determine whether we're close enough to the next to to consider it completed + if pos.xy().distance_squared(closest_tgt.xy()) < traversal_tolerance.powf(2.0) + && closest_tgt.z - pos.z < 0.2 + && closest_tgt.z - pos.z > -2.2 && vel.z <= 0.0 + // Only consider the node reached if there's nothing solid between us and it && vol - .ray(pos + Vec3::unit_z() * 0.5, next_tgt + Vec3::unit_z() * 0.5) + .ray(pos + Vec3::unit_z() * 1.5, closest_tgt + Vec3::unit_z() * 1.5) .until(|block| block.is_solid()) .cast() .0 - > pos.distance(next_tgt) * 0.9 + > pos.distance(closest_tgt) * 0.9 + && self.next_idx < self.path.len() { + // Node completed, move on to the next one self.next_idx += 1; + } else { + // The next node hasn't been reached yet, use it as a target + break (next0, next1, next0_tgt); } + }; - let line = LineSegment2 { - start: pos.xy(), - end: pos.xy() + vel.xy() * 100.0, - }; + let line = LineSegment2 { + start: pos.xy(), + end: pos.xy() + vel.xy() * 100.0, + }; - let align = |block_pos: Vec3| { - (0..2) - .map(|i| (0..2).map(move |j| Vec2::new(i, j))) - .flatten() - .map(|rpos| block_pos + rpos) - .map(|block_pos| { - let block_posf = block_pos.xy().map(|e| e as f32); - let proj = line.projected_point(block_posf); - let clamped = proj.clamped( - block_pos.xy().map(|e| e as f32), - block_pos.xy().map(|e| e as f32), - ); + // We don't always want to aim for the centre of block since this can create + // jerky zig-zag movement. This function attempts to find a position + // inside a target block's area that aligned nicely with our velocity. + // This has a twofold benefit: + // + // 1. Entities can move at any angle when + // running on a flat surface + // + // 2. We don't have to search diagonals when + // pathfinding - cartesian positions are enough since this code will + // make the entity move smoothly along them + let align = |block_pos: Vec3| { + (0..2) + .map(|i| (0..2).map(move |j| Vec2::new(i, j))) + .flatten() + .map(|rpos| block_pos + rpos) + .map(|block_pos| { + let block_posf = block_pos.xy().map(|e| e as f32); + let proj = line.projected_point(block_posf); + let clamped = proj.clamped( + block_pos.xy().map(|e| e as f32), + block_pos.xy().map(|e| e as f32), + ); - (proj.distance_squared(clamped), clamped) - }) - .min_by_key(|(d2, _)| (d2 * 1000.0) as i32) - .unwrap() - .1 - }; + (proj.distance_squared(clamped), clamped) + }) + .min_by_key(|(d2, _)| (d2 * 1000.0) as i32) + .unwrap() + .1 + }; - let cb = CubicBezier2 { - start: pos.xy(), - ctrl0: pos.xy() + vel.xy().try_normalized().unwrap_or_else(Vec2::zero), - ctrl1: align(next0), - end: align(next1), - }; + let cb = CubicBezier2 { + start: pos.xy(), + ctrl0: pos.xy() + vel.xy().try_normalized().unwrap_or_else(Vec2::zero) * 1.25, + ctrl1: align(next0), + end: align(next1), + }; - let tgt2d = cb.evaluate(0.5); - let tgt = Vec3::from(tgt2d) + Vec3::unit_z() * next_tgt.z; - let tgt_dir = (tgt - pos) - .xy() - .try_normalized() - .unwrap_or_else(Vec2::unit_y); - let next_dir = cb - .evaluate_derivative(0.5) - .try_normalized() - .unwrap_or(tgt_dir); + // Use a cubic spline of the next few targets to come up with a sensible target + // position. We want to use a position that gives smooth movement but is + // also accurate enough to avoid the agent getting stuck under ledges or + // falling off walls. + let tgt2d = cb.evaluate(0.5); + let tgt = Vec3::from(tgt2d) + Vec3::unit_z() * next_tgt.z; + let tgt_dir = (tgt - pos) + .xy() + .try_normalized() + .unwrap_or_else(Vec2::unit_y); + let next_dir = cb + .evaluate_derivative(0.5) + .try_normalized() + .unwrap_or(tgt_dir); - //let vel_dir = vel.xy().try_normalized().unwrap_or(Vec2::zero()); - //let avg_dir = (tgt_dir * 0.2 + vel_dir * - // 0.8).try_normalized().unwrap_or(Vec2::zero()); let bearing = - // Vec3::::from(avg_dir * (tgt - pos).xy().magnitude()) + Vec3::unit_z() * - // (tgt.z - pos.z); + //let vel_dir = vel.xy().try_normalized().unwrap_or(Vec2::zero()); + //let avg_dir = (tgt_dir * 0.2 + vel_dir * + // 0.8).try_normalized().unwrap_or(Vec2::zero()); let bearing = + // Vec3::::from(avg_dir * (tgt - pos).xy().magnitude()) + Vec3::unit_z() * + // (tgt.z - pos.z); - Some(( - tgt - pos, - next_dir - .dot(vel.xy().try_normalized().unwrap_or_else(Vec2::zero)) - .max(0.0) - * 0.75 - + 0.25, - )) - } + Some(( + tgt - pos, + // Control the entity's speed to hopefully stop us falling off walls on sharp corners. + // This code is very imperfect: it does its best but it can still fail for particularly + // fast entities. + next_dir + .dot(vel.xy().try_normalized().unwrap_or_else(Vec2::zero)) + .max(0.0) + * 0.75 + + 0.25, + )) } } @@ -186,33 +224,53 @@ impl Chaser { { let pos_to_tgt = pos.distance(tgt); + // If we're already close to the target then there's nothing to do if ((pos - tgt) * Vec3::new(1.0, 1.0, 2.0)).magnitude_squared() < min_dist.powf(2.0) { + self.route = None; return None; } let bearing = if let Some(end) = self.route.as_ref().and_then(|r| r.path().end().copied()) { let end_to_tgt = end.map(|e| e as f32).distance(tgt); - if end_to_tgt > pos_to_tgt * 0.3 + 5.0 || thread_rng().gen::() < 0.005 { + // If the target has moved significantly since the path was generated then it's + // time to search for a new path. Also, do this randomly from time + // to time to avoid any edge cases that cause us to get stuck. In + // theory this shouldn't happen, but in practice the world is full + // of unpredictable obstacles that are more than willing to mess up + // our day. TODO: Come up with a better heuristic for this + if end_to_tgt > pos_to_tgt * 0.3 + 5.0 + /* || thread_rng().gen::() < 0.005 */ + { None } else { self.route .as_mut() .and_then(|r| r.traverse(vol, pos, vel, traversal_tolerance)) + // In theory this filter isn't needed, but in practice agents often try to take + // stale paths that start elsewhere. This code makes sure that we're only using + // paths that start near us, avoiding the agent doubling back to chase a stale + // path. + .filter(|(bearing, _)| bearing.xy() + .magnitude_squared() < (traversal_tolerance * 3.0).powf(2.0)) } } else { None }; - // TODO: What happens when we get stuck? if let Some(bearing) = bearing { Some(bearing) } else { + // Only search for a path if the target has moved from their last position. We + // don't want to be thrashing the pathfinding code for targets that + // we're unable to access! if self .last_search_tgt .map(|last_tgt| last_tgt.distance(tgt) > pos_to_tgt * 0.15 + 5.0) .unwrap_or(true) + || self.route.is_none() { let (start_pos, path) = find_path(&mut self.astar, vol, pos, tgt); + // Don't use a stale path if start_pos.distance_squared(pos) < 4.0f32.powf(2.0) { self.route = path.map(Route::from); } else { @@ -331,14 +389,14 @@ where .unwrap_or(true))) }) .map(move |(pos, dir)| pos + dir) - .chain( - DIAGONALS - .iter() - .filter(move |(dir, [a, b])| { - is_walkable(&(pos + *dir)) && walkable[*a] && walkable[*b] - }) - .map(move |(dir, _)| pos + *dir), - ) + // .chain( + // DIAGONALS + // .iter() + // .filter(move |(dir, [a, b])| { + // is_walkable(&(pos + *dir)) && walkable[*a] && + // walkable[*b] }) + // .map(move |(dir, _)| pos + *dir), + // ) }; let crow_line = LineSegment2 { @@ -347,6 +405,8 @@ where }; let transition = |a: &Vec3, b: &Vec3| { + // Modify the heuristic a little in order to prefer paths that take us on a + // straight line toward our target. This means we get smoother movement. 1.0 + crow_line.distance_to_point(b.xy().map(|e| e as f32)) * 0.025 + (b.z - a.z - 1).max(0) as f32 * 3.0 }; diff --git a/common/src/sys/agent.rs b/common/src/sys/agent.rs index 5fcfd5d47a..8e96fa54e9 100644 --- a/common/src/sys/agent.rs +++ b/common/src/sys/agent.rs @@ -126,7 +126,7 @@ impl<'a> System<'a> for Sys { // and so can afford to be less precise when trying to move around // the world (especially since they would otherwise get stuck on // obstacles that smaller entities would not). - let traversal_tolerance = scale + vel.0.magnitude() * 0.3; + let traversal_tolerance = scale + vel.0.magnitude() * 0.25; let mut do_idle = false; let mut choose_target = false; From ac30fcbd0ee968fa73c22e21b1746f358cef9773 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Wed, 8 Jul 2020 13:42:13 +0100 Subject: [PATCH 08/85] Commented unused but potentially useful code --- common/src/path.rs | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/common/src/path.rs b/common/src/path.rs index b51bea7cb3..051d68846f 100644 --- a/common/src/path.rs +++ b/common/src/path.rs @@ -4,7 +4,6 @@ use crate::{ vol::{BaseVol, ReadVol}, }; use hashbrown::hash_map::DefaultHashBuilder; -use rand::{thread_rng, Rng}; use std::iter::FromIterator; use vek::*; @@ -349,23 +348,23 @@ where Vec3::new(0, 0, -1), // Downwards ]; - let walkable = [ - is_walkable(&(pos + Vec3::new(1, 0, 0))), - is_walkable(&(pos + Vec3::new(-1, 0, 0))), - is_walkable(&(pos + Vec3::new(0, 1, 0))), - is_walkable(&(pos + Vec3::new(0, -1, 0))), - ]; + // let walkable = [ + // is_walkable(&(pos + Vec3::new(1, 0, 0))), + // is_walkable(&(pos + Vec3::new(-1, 0, 0))), + // is_walkable(&(pos + Vec3::new(0, 1, 0))), + // is_walkable(&(pos + Vec3::new(0, -1, 0))), + // ]; - const DIAGONALS: [(Vec3, [usize; 2]); 8] = [ - (Vec3::new(1, 1, 0), [0, 2]), - (Vec3::new(-1, 1, 0), [1, 2]), - (Vec3::new(1, -1, 0), [0, 3]), - (Vec3::new(-1, -1, 0), [1, 3]), - (Vec3::new(1, 1, 1), [0, 2]), - (Vec3::new(-1, 1, 1), [1, 2]), - (Vec3::new(1, -1, 1), [0, 3]), - (Vec3::new(-1, -1, 1), [1, 3]), - ]; + // const DIAGONALS: [(Vec3, [usize; 2]); 8] = [ + // (Vec3::new(1, 1, 0), [0, 2]), + // (Vec3::new(-1, 1, 0), [1, 2]), + // (Vec3::new(1, -1, 0), [0, 3]), + // (Vec3::new(-1, -1, 0), [1, 3]), + // (Vec3::new(1, 1, 1), [0, 2]), + // (Vec3::new(-1, 1, 1), [1, 2]), + // (Vec3::new(1, -1, 1), [0, 3]), + // (Vec3::new(-1, -1, 1), [1, 3]), + // ]; DIRS.iter() .map(move |dir| (pos, dir)) From 951a977b2ff7a3fa64f419b9d5ab20ec3f8973bd Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Fri, 10 Jul 2020 00:43:11 +0100 Subject: [PATCH 09/85] Improved hill path following --- common/src/path.rs | 165 +++++++++++++++++++++++++--------------- common/src/sys/agent.rs | 10 ++- 2 files changed, 110 insertions(+), 65 deletions(-) diff --git a/common/src/path.rs b/common/src/path.rs index 051d68846f..4d91693f2e 100644 --- a/common/src/path.rs +++ b/common/src/path.rs @@ -69,6 +69,7 @@ impl Route { vol: &V, pos: Vec3, vel: Vec3, + on_ground: bool, traversal_tolerance: f32, ) -> Option<(Vec3, f32)> where @@ -85,42 +86,51 @@ impl Route { } let next1 = self.next(1).unwrap_or(next0); - let next0_tgt = next0.map(|e| e as f32) + Vec3::new(0.5, 0.5, 0.0); - let next1_tgt = next1.map(|e| e as f32) + Vec3::new(0.5, 0.5, 0.0); - - // We might be able to skip a node in some cases to avoid doubling-back - let closest_tgt = if next0_tgt.distance_squared(pos) < next1_tgt.distance_squared(pos) { - next0_tgt - } else { - next1_tgt - }; + let next_tgt = next0.map(|e| e as f32) + Vec3::new(0.5, 0.5, 0.0); // Determine whether we're close enough to the next to to consider it completed - if pos.xy().distance_squared(closest_tgt.xy()) < traversal_tolerance.powf(2.0) - && closest_tgt.z - pos.z < 0.2 - && closest_tgt.z - pos.z > -2.2 + if pos.xy().distance_squared(next_tgt.xy()) < traversal_tolerance.powf(2.0) + && (pos.z - next_tgt.z > 1.2 || (pos.z - next_tgt.z > -0.2 && on_ground)) + && pos.z - next_tgt.z < 2.2 && vel.z <= 0.0 // Only consider the node reached if there's nothing solid between us and it && vol - .ray(pos + Vec3::unit_z() * 1.5, closest_tgt + Vec3::unit_z() * 1.5) + .ray(pos + Vec3::unit_z() * 1.5, next_tgt + Vec3::unit_z() * 1.5) .until(|block| block.is_solid()) .cast() .0 - > pos.distance(closest_tgt) * 0.9 + > pos.distance(next_tgt) * 0.9 && self.next_idx < self.path.len() { // Node completed, move on to the next one self.next_idx += 1; } else { // The next node hasn't been reached yet, use it as a target - break (next0, next1, next0_tgt); + break (next0, next1, next_tgt); } }; - let line = LineSegment2 { - start: pos.xy(), - end: pos.xy() + vel.xy() * 100.0, - }; + fn gradient(line: LineSegment2) -> f32 { + let r = (line.start.y - line.end.y) / (line.start.x - line.end.x); + if r.is_nan() { 100000.0 } else { r } + } + + fn intersect(a: LineSegment2, b: LineSegment2) -> Option> { + let ma = gradient(a); + let mb = gradient(b); + + let ca = a.start.y - ma * a.start.x; + let cb = b.start.y - mb * b.start.x; + + if (ma - mb).abs() < 0.0001 || (ca - cb).abs() < 0.0001 { + None + } else { + let x = (cb - ca) / (ma - mb); + let y = ma * x + ca; + + Some(Vec2::new(x, y)) + } + } // We don't always want to aim for the centre of block since this can create // jerky zig-zag movement. This function attempts to find a position @@ -133,65 +143,92 @@ impl Route { // 2. We don't have to search diagonals when // pathfinding - cartesian positions are enough since this code will // make the entity move smoothly along them - let align = |block_pos: Vec3| { - (0..2) - .map(|i| (0..2).map(move |j| Vec2::new(i, j))) - .flatten() - .map(|rpos| block_pos + rpos) - .map(|block_pos| { - let block_posf = block_pos.xy().map(|e| e as f32); - let proj = line.projected_point(block_posf); - let clamped = proj.clamped( - block_pos.xy().map(|e| e as f32), - block_pos.xy().map(|e| e as f32), - ); + let corners = [ + Vec2::new(0, 0), + Vec2::new(1, 0), + Vec2::new(1, 1), + Vec2::new(0, 1), + Vec2::new(0, 0), // Repeated start + ]; - (proj.distance_squared(clamped), clamped) - }) - .min_by_key(|(d2, _)| (d2 * 1000.0) as i32) - .unwrap() - .1 + let vel_line = LineSegment2 { + start: pos.xy(), + end: pos.xy() + vel.xy() * 100.0, }; - let cb = CubicBezier2 { + let align = |block_pos: Vec3, precision: f32| { + let lerp_block = |x, precision| Lerp::lerp(x, block_pos.xy().map(|e| e as f32), precision); + + (0..4) + .filter_map(|i| { + let edge_line = LineSegment2 { + start: lerp_block((block_pos.xy() + corners[i]).map(|e| e as f32), precision), + end: lerp_block((block_pos.xy() + corners[i + 1]).map(|e| e as f32), precision), + }; + intersect(vel_line, edge_line) + .filter(|intersect| intersect.clamped( + block_pos.xy().map(|e| e as f32), + block_pos.xy().map(|e| e as f32 + 1.0), + ).distance_squared(*intersect) < 0.001) + }) + .min_by_key(|intersect: &Vec2| (intersect.distance_squared(vel_line.end) * 1000.0) as i32) + .unwrap_or_else(|| (0..2) + .map(|i| (0..2).map(move |j| Vec2::new(i, j))) + .flatten() + .map(|rpos| block_pos + rpos) + .map(|block_pos| { + let block_posf = block_pos.xy().map(|e| e as f32); + let proj = vel_line.projected_point(block_posf); + let clamped = lerp_block(proj.clamped( + block_pos.xy().map(|e| e as f32), + block_pos.xy().map(|e| e as f32), + ), precision); + + (proj.distance_squared(clamped), clamped) + }) + .min_by_key(|(d2, _)| (d2 * 1000.0) as i32) + .unwrap() + .1) + }; + + let bez = CubicBezier2 { start: pos.xy(), - ctrl0: pos.xy() + vel.xy().try_normalized().unwrap_or_else(Vec2::zero) * 1.25, - ctrl1: align(next0), - end: align(next1), + ctrl0: pos.xy() + vel.xy().try_normalized().unwrap_or(Vec2::zero()) * 1.0, + ctrl1: align(next0, 1.0), + end: align(next1, 1.0), }; // Use a cubic spline of the next few targets to come up with a sensible target // position. We want to use a position that gives smooth movement but is // also accurate enough to avoid the agent getting stuck under ledges or // falling off walls. - let tgt2d = cb.evaluate(0.5); - let tgt = Vec3::from(tgt2d) + Vec3::unit_z() * next_tgt.z; - let tgt_dir = (tgt - pos) - .xy() + let next_dir = bez + .evaluate_derivative(0.85) .try_normalized() - .unwrap_or_else(Vec2::unit_y); - let next_dir = cb - .evaluate_derivative(0.5) - .try_normalized() - .unwrap_or(tgt_dir); + .unwrap_or(Vec2::zero()); + let straight_factor = next_dir + .dot(vel.xy().try_normalized().unwrap_or(next_dir)) + .max(0.0) + .powf(2.0); - //let vel_dir = vel.xy().try_normalized().unwrap_or(Vec2::zero()); - //let avg_dir = (tgt_dir * 0.2 + vel_dir * - // 0.8).try_normalized().unwrap_or(Vec2::zero()); let bearing = - // Vec3::::from(avg_dir * (tgt - pos).xy().magnitude()) + Vec3::unit_z() * - // (tgt.z - pos.z); + let bez = CubicBezier2 { + start: pos.xy(), + ctrl0: pos.xy() + vel.xy().try_normalized().unwrap_or(Vec2::zero()) * 1.0, + ctrl1: align(next0, (1.0 - straight_factor * if (next0.z as f32 - pos.z).abs() < 0.25 { 1.0 } else { 0.0 }).max(0.1)), + end: align(next1, 1.0), + }; + + let tgt2d = bez.evaluate(if (next0.z as f32 - pos.z).abs() < 0.25 { 0.25 } else { 0.5 }); + let tgt = Vec3::from(tgt2d) + Vec3::unit_z() * next_tgt.z; Some(( tgt - pos, // Control the entity's speed to hopefully stop us falling off walls on sharp corners. // This code is very imperfect: it does its best but it can still fail for particularly // fast entities. - next_dir - .dot(vel.xy().try_normalized().unwrap_or_else(Vec2::zero)) - .max(0.0) - * 0.75 - + 0.25, + straight_factor * 0.75 + 0.25, )) + .filter(|(bearing, _)| bearing.z < 2.1) } } @@ -214,6 +251,7 @@ impl Chaser { vol: &V, pos: Vec3, vel: Vec3, + on_ground: bool, tgt: Vec3, min_dist: f32, traversal_tolerance: f32, @@ -244,7 +282,7 @@ impl Chaser { } else { self.route .as_mut() - .and_then(|r| r.traverse(vol, pos, vel, traversal_tolerance)) + .and_then(|r| r.traverse(vol, pos, vel, on_ground, traversal_tolerance)) // In theory this filter isn't needed, but in practice agents often try to take // stale paths that start elsewhere. This code makes sure that we're only using // paths that start near us, avoiding the agent doubling back to chase a stale @@ -256,8 +294,8 @@ impl Chaser { None }; - if let Some(bearing) = bearing { - Some(bearing) + if let Some((bearing, speed)) = bearing { + Some((bearing, speed)) } else { // Only search for a path if the target has moved from their last position. We // don't want to be thrashing the pathfinding code for targets that @@ -266,6 +304,7 @@ impl Chaser { .last_search_tgt .map(|last_tgt| last_tgt.distance(tgt) > pos_to_tgt * 0.15 + 5.0) .unwrap_or(true) + || self.astar.is_some() || self.route.is_none() { let (start_pos, path) = find_path(&mut self.astar, vol, pos, tgt); @@ -407,7 +446,7 @@ where // Modify the heuristic a little in order to prefer paths that take us on a // straight line toward our target. This means we get smoother movement. 1.0 + crow_line.distance_to_point(b.xy().map(|e| e as f32)) * 0.025 - + (b.z - a.z - 1).max(0) as f32 * 3.0 + + (b.z - a.z - 1).max(0) as f32 * 10.0 }; let satisfied = |pos: &Vec3| pos == &end; diff --git a/common/src/sys/agent.rs b/common/src/sys/agent.rs index 8e96fa54e9..0a3797dea1 100644 --- a/common/src/sys/agent.rs +++ b/common/src/sys/agent.rs @@ -4,7 +4,7 @@ use crate::{ agent::Activity, item::{tool::ToolKind, ItemKind}, Agent, Alignment, CharacterState, ChatMsg, ControlAction, Controller, Loadout, MountState, - Ori, Pos, Scale, Stats, Vel, + Ori, Pos, Scale, Stats, Vel, PhysicsState, }, event::{EventBus, ServerEvent}, path::Chaser, @@ -38,6 +38,7 @@ impl<'a> System<'a> for Sys { ReadStorage<'a, Stats>, ReadStorage<'a, Loadout>, ReadStorage<'a, CharacterState>, + ReadStorage<'a, PhysicsState>, ReadStorage<'a, Uid>, ReadExpect<'a, TerrainGrid>, ReadStorage<'a, Alignment>, @@ -62,6 +63,7 @@ impl<'a> System<'a> for Sys { stats, loadouts, character_states, + physics_states, uids, terrain, alignments, @@ -78,6 +80,7 @@ impl<'a> System<'a> for Sys { alignment, loadout, character_state, + physics_state, uid, agent, controller, @@ -90,6 +93,7 @@ impl<'a> System<'a> for Sys { alignments.maybe(), &loadouts, &character_states, + &physics_states, &uids, &mut agents, &mut controllers, @@ -126,7 +130,7 @@ impl<'a> System<'a> for Sys { // and so can afford to be less precise when trying to move around // the world (especially since they would otherwise get stuck on // obstacles that smaller entities would not). - let traversal_tolerance = scale + vel.0.magnitude() * 0.25; + let traversal_tolerance = scale + vel.0.xy().magnitude() * 0.2; let mut do_idle = false; let mut choose_target = false; @@ -198,6 +202,7 @@ impl<'a> System<'a> for Sys { &*terrain, pos.0, vel.0, + physics_state.on_ground, tgt_pos.0, AVG_FOLLOW_DIST, traversal_tolerance, @@ -314,6 +319,7 @@ impl<'a> System<'a> for Sys { &*terrain, pos.0, vel.0, + physics_state.on_ground, tgt_pos.0, 1.25, traversal_tolerance, From cf69d0c5d885eb2754095cb6c30775b638ec8753 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Fri, 10 Jul 2020 15:00:20 +0100 Subject: [PATCH 10/85] Added minimap compass --- CHANGELOG.md | 1 + common/src/path.rs | 172 ++++++++++++++++++++++++++----------- common/src/states/utils.rs | 6 +- common/src/sys/agent.rs | 11 ++- voxygen/src/hud/minimap.rs | 46 +++++++++- voxygen/src/hud/mod.rs | 4 +- 6 files changed, 182 insertions(+), 58 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d2a842d4df..66cec07206 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -42,6 +42,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Training dummy items - Added spin attack for axe - Creature specific stats +- Minimap compass ### Changed diff --git a/common/src/path.rs b/common/src/path.rs index 4d91693f2e..cf8e6b8e73 100644 --- a/common/src/path.rs +++ b/common/src/path.rs @@ -4,6 +4,7 @@ use crate::{ vol::{BaseVol, ReadVol}, }; use hashbrown::hash_map::DefaultHashBuilder; +use rand::prelude::*; use std::iter::FromIterator; use vek::*; @@ -71,11 +72,12 @@ impl Route { vel: Vec3, on_ground: bool, traversal_tolerance: f32, + slow_factor: f32, ) -> Option<(Vec3, f32)> where V: BaseVol + ReadVol, { - let (next0, next1, next_tgt) = loop { + let (next0, next1, next_tgt, be_precise) = loop { let next0 = self .next(0) .unwrap_or_else(|| pos.map(|e| e.floor() as i32)); @@ -85,28 +87,57 @@ impl Route { return None; } + let diagonals = [ + Vec2::new(1, 0), + Vec2::new(1, 1), + Vec2::new(0, 1), + Vec2::new(-1, 1), + Vec2::new(-1, 0), + Vec2::new(-1, -1), + Vec2::new(0, -1), + Vec2::new(1, -1), + ]; + let next1 = self.next(1).unwrap_or(next0); - let next_tgt = next0.map(|e| e as f32) + Vec3::new(0.5, 0.5, 0.0); + + let be_precise = diagonals.iter().any(|pos| { + !walkable(vol, next0 + Vec3::new(pos.x, pos.y, 0)) + && !walkable(vol, next0 + Vec3::new(pos.x, pos.y, -1)) + && !walkable(vol, next0 + Vec3::new(pos.x, pos.y, -2)) + && !walkable(vol, next0 + Vec3::new(pos.x, pos.y, 1)) + }); + + let next0_tgt = next0.map(|e| e as f32) + Vec3::new(0.5, 0.5, 0.0); + let next1_tgt = next1.map(|e| e as f32) + Vec3::new(0.5, 0.5, 0.0); + let next_tgt = next0_tgt; + + // Maybe skip a node (useful with traversing downhill) + let closest_tgt = if next0_tgt.distance_squared(pos) < next1_tgt.distance_squared(pos) { + next0_tgt + } else { + next1_tgt + }; // Determine whether we're close enough to the next to to consider it completed - if pos.xy().distance_squared(next_tgt.xy()) < traversal_tolerance.powf(2.0) - && (pos.z - next_tgt.z > 1.2 || (pos.z - next_tgt.z > -0.2 && on_ground)) - && pos.z - next_tgt.z < 2.2 + let dist_sqrd = pos.xy().distance_squared(closest_tgt.xy()); + if dist_sqrd < traversal_tolerance.powf(2.0) * if be_precise { 0.25 } else { 1.0 } + && (pos.z - closest_tgt.z > 1.2 || (pos.z - closest_tgt.z > -0.2 && on_ground)) + && (pos.z - closest_tgt.z < 1.2 || (pos.z - closest_tgt.z < 2.9 && vel.z < -0.05)) && vel.z <= 0.0 // Only consider the node reached if there's nothing solid between us and it && vol - .ray(pos + Vec3::unit_z() * 1.5, next_tgt + Vec3::unit_z() * 1.5) + .ray(pos + Vec3::unit_z() * 1.5, closest_tgt + Vec3::unit_z() * 1.5) .until(|block| block.is_solid()) .cast() .0 - > pos.distance(next_tgt) * 0.9 + > pos.distance(closest_tgt) * 0.9 && self.next_idx < self.path.len() { // Node completed, move on to the next one self.next_idx += 1; } else { // The next node hasn't been reached yet, use it as a target - break (next0, next1, next_tgt); + break (next0, next1, next_tgt, be_precise); } }; @@ -157,38 +188,56 @@ impl Route { }; let align = |block_pos: Vec3, precision: f32| { - let lerp_block = |x, precision| Lerp::lerp(x, block_pos.xy().map(|e| e as f32), precision); + let lerp_block = + |x, precision| Lerp::lerp(x, block_pos.xy().map(|e| e as f32), precision); (0..4) .filter_map(|i| { let edge_line = LineSegment2 { - start: lerp_block((block_pos.xy() + corners[i]).map(|e| e as f32), precision), - end: lerp_block((block_pos.xy() + corners[i + 1]).map(|e| e as f32), precision), + start: lerp_block( + (block_pos.xy() + corners[i]).map(|e| e as f32), + precision, + ), + end: lerp_block( + (block_pos.xy() + corners[i + 1]).map(|e| e as f32), + precision, + ), }; - intersect(vel_line, edge_line) - .filter(|intersect| intersect.clamped( - block_pos.xy().map(|e| e as f32), - block_pos.xy().map(|e| e as f32 + 1.0), - ).distance_squared(*intersect) < 0.001) + intersect(vel_line, edge_line).filter(|intersect| { + intersect + .clamped( + block_pos.xy().map(|e| e as f32), + block_pos.xy().map(|e| e as f32 + 1.0), + ) + .distance_squared(*intersect) + < 0.001 + }) }) - .min_by_key(|intersect: &Vec2| (intersect.distance_squared(vel_line.end) * 1000.0) as i32) - .unwrap_or_else(|| (0..2) - .map(|i| (0..2).map(move |j| Vec2::new(i, j))) + .min_by_key(|intersect: &Vec2| { + (intersect.distance_squared(vel_line.end) * 1000.0) as i32 + }) + .unwrap_or_else(|| { + (0..2) + .map(|i| (0..2).map(move |j| Vec2::new(i, j))) .flatten() .map(|rpos| block_pos + rpos) .map(|block_pos| { let block_posf = block_pos.xy().map(|e| e as f32); let proj = vel_line.projected_point(block_posf); - let clamped = lerp_block(proj.clamped( - block_pos.xy().map(|e| e as f32), - block_pos.xy().map(|e| e as f32), - ), precision); + let clamped = lerp_block( + proj.clamped( + block_pos.xy().map(|e| e as f32), + block_pos.xy().map(|e| e as f32), + ), + precision, + ); (proj.distance_squared(clamped), clamped) }) .min_by_key(|(d2, _)| (d2 * 1000.0) as i32) .unwrap() - .1) + .1 + }) }; let bez = CubicBezier2 { @@ -214,21 +263,38 @@ impl Route { let bez = CubicBezier2 { start: pos.xy(), ctrl0: pos.xy() + vel.xy().try_normalized().unwrap_or(Vec2::zero()) * 1.0, - ctrl1: align(next0, (1.0 - straight_factor * if (next0.z as f32 - pos.z).abs() < 0.25 { 1.0 } else { 0.0 }).max(0.1)), + ctrl1: align( + next0, + (1.0 - straight_factor + * if (next0.z as f32 - pos.z).abs() < 0.25 && !be_precise { + 1.0 + } else { + 0.0 + }) + .max(0.1), + ), end: align(next1, 1.0), }; - let tgt2d = bez.evaluate(if (next0.z as f32 - pos.z).abs() < 0.25 { 0.25 } else { 0.5 }); - let tgt = Vec3::from(tgt2d) + Vec3::unit_z() * next_tgt.z; + let tgt2d = bez.evaluate(if (next0.z as f32 - pos.z).abs() < 0.25 { + 0.25 + } else { + 0.5 + }); + let tgt = if be_precise { + next_tgt + } else { + Vec3::from(tgt2d) + Vec3::unit_z() * next_tgt.z + }; Some(( tgt - pos, // Control the entity's speed to hopefully stop us falling off walls on sharp corners. // This code is very imperfect: it does its best but it can still fail for particularly // fast entities. - straight_factor * 0.75 + 0.25, + straight_factor * slow_factor + (1.0 - slow_factor), )) - .filter(|(bearing, _)| bearing.z < 2.1) + .filter(|(bearing, _)| bearing.z < 2.1) } } @@ -255,6 +321,7 @@ impl Chaser { tgt: Vec3, min_dist: f32, traversal_tolerance: f32, + slow_factor: f32, ) -> Option<(Vec3, f32)> where V: BaseVol + ReadVol, @@ -282,13 +349,14 @@ impl Chaser { } else { self.route .as_mut() - .and_then(|r| r.traverse(vol, pos, vel, on_ground, traversal_tolerance)) + .and_then(|r| r.traverse(vol, pos, vel, on_ground, traversal_tolerance, slow_factor)) // In theory this filter isn't needed, but in practice agents often try to take // stale paths that start elsewhere. This code makes sure that we're only using // paths that start near us, avoiding the agent doubling back to chase a stale // path. .filter(|(bearing, _)| bearing.xy() - .magnitude_squared() < (traversal_tolerance * 3.0).powf(2.0)) + .magnitude_squared() < 1.75f32.powf(2.0) + && thread_rng().gen::() > 0.025) } } else { None @@ -321,6 +389,24 @@ impl Chaser { } } +#[allow(clippy::float_cmp)] // TODO: Pending review in #587 +fn walkable(vol: &V, pos: Vec3) -> bool +where + V: BaseVol + ReadVol, +{ + vol.get(pos - Vec3::new(0, 0, 1)) + .map(|b| b.is_solid() && b.get_height() == 1.0) + .unwrap_or(false) + && vol + .get(pos + Vec3::new(0, 0, 0)) + .map(|b| !b.is_solid()) + .unwrap_or(true) + && vol + .get(pos + Vec3::new(0, 0, 1)) + .map(|b| !b.is_solid()) + .unwrap_or(true) +} + #[allow(clippy::float_cmp)] // TODO: Pending review in #587 fn find_path( astar: &mut Option, DefaultHashBuilder>>, @@ -331,19 +417,7 @@ fn find_path( where V: BaseVol + ReadVol, { - let is_walkable = |pos: &Vec3| { - vol.get(*pos - Vec3::new(0, 0, 1)) - .map(|b| b.is_solid() && b.get_height() == 1.0) - .unwrap_or(false) - && vol - .get(*pos + Vec3::new(0, 0, 0)) - .map(|b| !b.is_solid()) - .unwrap_or(true) - && vol - .get(*pos + Vec3::new(0, 0, 1)) - .map(|b| !b.is_solid()) - .unwrap_or(true) - }; + let is_walkable = |pos: &Vec3| walkable(vol, *pos); let get_walkable_z = |pos| { let mut z_incr = 0; for _ in 0..32 { @@ -437,12 +511,12 @@ where // ) }; - let crow_line = LineSegment2 { - start: startf.xy(), - end: endf.xy(), - }; - let transition = |a: &Vec3, b: &Vec3| { + let crow_line = LineSegment2 { + start: startf.xy(), + end: endf.xy(), + }; + // Modify the heuristic a little in order to prefer paths that take us on a // straight line toward our target. This means we get smoother movement. 1.0 + crow_line.distance_to_point(b.xy().map(|e| e as f32)) * 0.025 diff --git a/common/src/states/utils.rs b/common/src/states/utils.rs index 7ebec30dc4..ce01809f80 100644 --- a/common/src/states/utils.rs +++ b/common/src/states/utils.rs @@ -31,9 +31,9 @@ impl Body { pub fn base_accel(&self) -> f32 { match self { Body::Humanoid(_) => 100.0, - Body::QuadrupedSmall(_) => 80.0, + Body::QuadrupedSmall(_) => 85.0, Body::QuadrupedMedium(_) => 180.0, - Body::BirdMedium(_) => 70.0, + Body::BirdMedium(_) => 80.0, Body::FishMedium(_) => 50.0, Body::Dragon(_) => 250.0, Body::BirdSmall(_) => 75.0, @@ -41,7 +41,7 @@ impl Body { Body::BipedLarge(_) => 120.0, Body::Object(_) => 40.0, Body::Golem(_) => 130.0, - Body::Critter(_) => 65.0, + Body::Critter(_) => 85.0, Body::QuadrupedLow(_) => 120.0, } } diff --git a/common/src/sys/agent.rs b/common/src/sys/agent.rs index 0a3797dea1..aa6159f0a9 100644 --- a/common/src/sys/agent.rs +++ b/common/src/sys/agent.rs @@ -3,8 +3,8 @@ use crate::{ self, agent::Activity, item::{tool::ToolKind, ItemKind}, - Agent, Alignment, CharacterState, ChatMsg, ControlAction, Controller, Loadout, MountState, - Ori, Pos, Scale, Stats, Vel, PhysicsState, + Agent, Alignment, Body, CharacterState, ChatMsg, ControlAction, Controller, Loadout, + MountState, Ori, PhysicsState, Pos, Scale, Stats, Vel, }, event::{EventBus, ServerEvent}, path::Chaser, @@ -42,6 +42,7 @@ impl<'a> System<'a> for Sys { ReadStorage<'a, Uid>, ReadExpect<'a, TerrainGrid>, ReadStorage<'a, Alignment>, + ReadStorage<'a, Body>, WriteStorage<'a, Agent>, WriteStorage<'a, Controller>, ReadStorage<'a, MountState>, @@ -67,6 +68,7 @@ impl<'a> System<'a> for Sys { uids, terrain, alignments, + bodies, mut agents, mut controllers, mount_states, @@ -81,6 +83,7 @@ impl<'a> System<'a> for Sys { loadout, character_state, physics_state, + body, uid, agent, controller, @@ -94,6 +97,7 @@ impl<'a> System<'a> for Sys { &loadouts, &character_states, &physics_states, + bodies.maybe(), &uids, &mut agents, &mut controllers, @@ -131,6 +135,7 @@ impl<'a> System<'a> for Sys { // the world (especially since they would otherwise get stuck on // obstacles that smaller entities would not). let traversal_tolerance = scale + vel.0.xy().magnitude() * 0.2; + let slow_factor = body.map(|b| b.base_accel() / 250.0).unwrap_or(0.0).min(1.0); let mut do_idle = false; let mut choose_target = false; @@ -206,6 +211,7 @@ impl<'a> System<'a> for Sys { tgt_pos.0, AVG_FOLLOW_DIST, traversal_tolerance, + slow_factor, ) { inputs.move_dir = bearing.xy().try_normalized().unwrap_or(Vec2::zero()) @@ -323,6 +329,7 @@ impl<'a> System<'a> for Sys { tgt_pos.0, 1.25, traversal_tolerance, + slow_factor, ) { inputs.move_dir = Vec2::from(bearing) .try_normalized() diff --git a/voxygen/src/hud/minimap.rs b/voxygen/src/hud/minimap.rs index 3a0225539b..efc330ce7b 100644 --- a/voxygen/src/hud/minimap.rs +++ b/voxygen/src/hud/minimap.rs @@ -8,7 +8,7 @@ use common::{comp, terrain::TerrainChunkSize, vol::RectVolSize}; use conrod_core::{ color, position, widget::{self, Button, Image, Rectangle, Text}, - widget_ids, Colorable, Positionable, Sizeable, Widget, WidgetCommon, + widget_ids, Color, Colorable, Positionable, Sizeable, Widget, WidgetCommon, }; use specs::WorldExt; use vek::*; @@ -23,7 +23,11 @@ widget_ids! { mmap_plus, mmap_minus, grid, - indicator + indicator, + mmap_north, + mmap_east, + mmap_south, + mmap_west, } } @@ -39,6 +43,7 @@ pub struct MiniMap<'a> { fonts: &'a ConrodVoxygenFonts, #[conrod(common_builder)] common: widget::CommonBuilder, + ori: Vec3, } impl<'a> MiniMap<'a> { @@ -49,6 +54,7 @@ impl<'a> MiniMap<'a> { rot_imgs: &'a ImgsRot, world_map: &'a (img_ids::Rotations, Vec2), fonts: &'a ConrodVoxygenFonts, + ori: Vec3, ) -> Self { Self { show, @@ -58,6 +64,7 @@ impl<'a> MiniMap<'a> { world_map, fonts, common: widget::CommonBuilder::default(), + ori, } } } @@ -195,10 +202,12 @@ impl<'a> Widget for MiniMap<'a> { [w_src, h_src], ); + let map_size = Vec2::new(170.0, 170.0); + // Map Image Image::new(world_map.source_north) .middle_of(state.ids.mmap_frame_bg) - .w_h(170.0 * SCALE, 170.0 * SCALE) + .w_h(map_size.x * SCALE, map_size.y * SCALE) .parent(state.ids.mmap_frame_bg) .source_rectangle(rect_src) .set(state.ids.grid, ui); @@ -212,6 +221,37 @@ impl<'a> Widget for MiniMap<'a> { .floating(true) .parent(ui.window) .set(state.ids.indicator, ui); + + // Compass directions + let dirs = [ + (Vec2::new(0.0, 1.0), state.ids.mmap_north, "N", true), + (Vec2::new(1.0, 0.0), state.ids.mmap_east, "E", false), + (Vec2::new(0.0, -1.0), state.ids.mmap_south, "S", false), + (Vec2::new(-1.0, 0.0), state.ids.mmap_west, "W", false), + ]; + for (dir, id, name, bold) in dirs.iter() { + let cardinal_dir = Vec2::unit_x().rotated_z(self.ori.x as f64) * dir.x + + Vec2::unit_y().rotated_z(self.ori.x as f64) * dir.y; + let clamped = (cardinal_dir * 3.0) + / (cardinal_dir * 3.0).map(|e| e.abs()).reduce_partial_max(); + let pos = clamped * (map_size * 0.75 - 10.0); + Text::new(name) + .x_y_position_relative_to( + state.ids.grid, + position::Relative::Scalar(pos.x), + position::Relative::Scalar(pos.y), + ) + .font_size(self.fonts.cyri.scale(18)) + .font_id(self.fonts.cyri.conrod_id) + .color(if *bold { + Color::Rgba(0.7, 0.3, 0.3, 1.0) + } else { + TEXT_COLOR + }) + .floating(true) + .parent(ui.window) + .set(*id, ui); + } } else { Image::new(self.imgs.mmap_frame_closed) .w_h(174.0 * SCALE, 18.0 * SCALE) diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index da04cedf2f..2768a7ab4b 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -598,6 +598,7 @@ impl Hud { debug_info: DebugInfo, dt: Duration, info: HudInfo, + camera: &Camera, ) -> Vec { let mut events = std::mem::replace(&mut self.events, Vec::new()); let (ref mut ui_widgets, ref mut tooltip_manager) = self.ui.set_widgets(); @@ -1497,6 +1498,7 @@ impl Hud { &self.rot_imgs, &self.world_map, &self.fonts, + camera.get_orientation(), ) .set(self.ids.minimap, ui_widgets) { @@ -2246,7 +2248,7 @@ impl Hud { if let Some(maybe_id) = self.to_focus.take() { self.ui.focus_widget(maybe_id); } - let events = self.update_layout(client, global_state, debug_info, dt, info); + let events = self.update_layout(client, global_state, debug_info, dt, info, camera); let camera::Dependents { view_mat, proj_mat, .. } = camera.dependents(); From 3c72022c81b28c2ea452dd6cdf0b74dd3f271f69 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Fri, 10 Jul 2020 18:21:34 +0100 Subject: [PATCH 11/85] Nicer north colour --- voxygen/src/hud/minimap.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/voxygen/src/hud/minimap.rs b/voxygen/src/hud/minimap.rs index efc330ce7b..c47a2aa420 100644 --- a/voxygen/src/hud/minimap.rs +++ b/voxygen/src/hud/minimap.rs @@ -244,7 +244,7 @@ impl<'a> Widget for MiniMap<'a> { .font_size(self.fonts.cyri.scale(18)) .font_id(self.fonts.cyri.conrod_id) .color(if *bold { - Color::Rgba(0.7, 0.3, 0.3, 1.0) + Color::Rgba(0.75, 0.0, 0.0, 1.0) } else { TEXT_COLOR }) From 72ec565678c7a287408545d02a886da78669356d Mon Sep 17 00:00:00 2001 From: Yusuf Bera Ertan Date: Sun, 12 Jul 2020 05:38:11 +0300 Subject: [PATCH 12/85] fix localization test --- .gitlab/CI/build.gitlab-ci.yml | 4 ++-- voxygen/tests/check_i18n_files.rs | 24 +++++++++++++++++++----- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/.gitlab/CI/build.gitlab-ci.yml b/.gitlab/CI/build.gitlab-ci.yml index 8717d04f0e..f051c3dd47 100644 --- a/.gitlab/CI/build.gitlab-ci.yml +++ b/.gitlab/CI/build.gitlab-ci.yml @@ -21,7 +21,7 @@ localization-status: extends: .recompile-branch stage: build variables: - GIT_DEPTH: 0 + GIT_DEPTH: 9999999999999 allow_failure: true script: - ln -s /dockercache/cache-all target @@ -110,4 +110,4 @@ opt-windows: opt-macos: extends: - .tmacos - - .optional-release \ No newline at end of file + - .optional-release diff --git a/voxygen/tests/check_i18n_files.rs b/voxygen/tests/check_i18n_files.rs index 1fbbbd7b51..2753a01766 100644 --- a/voxygen/tests/check_i18n_files.rs +++ b/voxygen/tests/check_i18n_files.rs @@ -108,7 +108,10 @@ fn generate_key_version<'a>( .iter() .for_each(|e: git2::BlameHunk| { for state in keys.values_mut() { - let line = state.key_line.unwrap(); + let line = match state.key_line { + Some(l) => l, + None => continue, + }; if line >= e.final_start_line() && line < e.final_start_line() + e.lines_in_hunk() { state.chuck_line_range = Some(( @@ -176,14 +179,25 @@ fn test_all_localizations<'a>() { // Find the localization entry state let current_blob = read_file_from_path(&repo, &head_ref, &relfile); - let current_loc: VoxygenLocalization = - from_bytes(current_blob.content()).expect("Expect to parse the RON file"); + let current_loc: VoxygenLocalization = match from_bytes(current_blob.content()) { + Ok(v) => v, + Err(e) => { + eprintln!("Could not parse RON file, skipping: {}", e); + continue; + }, + }; let mut current_i18n = generate_key_version(&repo, ¤t_loc, &relfile, ¤t_blob); for (ref_key, ref_state) in i18n_references.iter() { match current_i18n.get_mut(ref_key) { Some(state) => { - let commit_id = state.commit_id.unwrap(); - let ref_commit_id = ref_state.commit_id.unwrap(); + let commit_id = match state.commit_id { + Some(c) => c, + None => continue, + }; + let ref_commit_id = match ref_state.commit_id { + Some(c) => c, + None => continue, + }; if commit_id != ref_commit_id && !repo .graph_descendant_of(commit_id, ref_commit_id) From 95fd89b4f989213decb9f7f95627317b7e6bf4aa Mon Sep 17 00:00:00 2001 From: Yusuf Bera Ertan Date: Sun, 12 Jul 2020 14:54:03 +0300 Subject: [PATCH 13/85] add more error messages, add percentage calculation for each i18n file and an overall percentage result --- voxygen/tests/check_i18n_files.rs | 102 +++++++++++++++++++++++++----- 1 file changed, 86 insertions(+), 16 deletions(-) diff --git a/voxygen/tests/check_i18n_files.rs b/voxygen/tests/check_i18n_files.rs index 2753a01766..54adba608d 100644 --- a/voxygen/tests/check_i18n_files.rs +++ b/voxygen/tests/check_i18n_files.rs @@ -80,13 +80,10 @@ fn generate_key_version<'a>( .keys() .map(|k| (k.to_owned(), LocalizationEntryState::new())) .collect(); - let mut to_process: HashSet<&String> = localization.string_map.keys().map(|k| k).collect(); + let mut to_process: HashSet<&String> = localization.string_map.keys().collect(); // Find key start lines - for (line_nb, line) in std::str::from_utf8(file_blob.content()) - .expect("UTF-8 file") - .split('\n') - .enumerate() - { + let file_content = std::str::from_utf8(file_blob.content()).expect("Got non UTF-8 file"); + for (line_nb, line) in file_content.lines().enumerate() { let mut found_key = None; for key in to_process.iter() { @@ -107,10 +104,16 @@ fn generate_key_version<'a>( .expect("Impossible to generate the Git blame") .iter() .for_each(|e: git2::BlameHunk| { - for state in keys.values_mut() { + for (key, state) in keys.iter_mut() { let line = match state.key_line { Some(l) => l, - None => continue, + None => { + eprintln!( + "Key {} does not have a git line in it's state! Skipping key.", + key + ); + continue; + }, }; if line >= e.final_start_line() && line < e.final_start_line() + e.lines_in_hunk() { @@ -162,16 +165,20 @@ fn test_all_localizations<'a>() { // Read HEAD for the reference language file let i18n_en_blob = read_file_from_path(&repo, &head_ref, &en_i18n_path); - let loc: VoxygenLocalization = - from_bytes(i18n_en_blob.content()).expect("Expect to parse the RON file"); + let loc: VoxygenLocalization = from_bytes(i18n_en_blob.content()) + .expect("Expect to parse reference i18n RON file, can't proceed without it"); let i18n_references: HashMap = generate_key_version(&repo, &loc, &en_i18n_path, &i18n_en_blob); // Compare to other reference files + let mut overall_uptodate_entry_count = 0; + let mut overall_outdated_entry_count = 0; + let mut overall_untranslated_entry_count = 0; + let mut overall_real_entry_count = 0; + let i18n_files = i18n_files(&i18n_path); - for file in i18n_files { + for file in &i18n_files { let relfile = file.strip_prefix(&root_dir).unwrap(); - let mut uptodate_entries = 0; if relfile == en_i18n_path { continue; } @@ -182,7 +189,11 @@ fn test_all_localizations<'a>() { let current_loc: VoxygenLocalization = match from_bytes(current_blob.content()) { Ok(v) => v, Err(e) => { - eprintln!("Could not parse RON file, skipping: {}", e); + eprintln!( + "Could not parse {} RON file, skipping: {}", + relfile.to_string_lossy(), + e + ); continue; }, }; @@ -192,11 +203,25 @@ fn test_all_localizations<'a>() { Some(state) => { let commit_id = match state.commit_id { Some(c) => c, - None => continue, + None => { + eprintln!( + "Commit ID of key {} in i18n file {} is missing! Skipping key.", + ref_key, + relfile.to_string_lossy() + ); + continue; + }, }; let ref_commit_id = match ref_state.commit_id { Some(c) => c, - None => continue, + None => { + eprintln!( + "Commit ID of key {} in reference i18n file is missing! Skipping \ + key.", + ref_key + ); + continue; + }, }; if commit_id != ref_commit_id && !repo @@ -238,9 +263,25 @@ fn test_all_localizations<'a>() { let mut sorted_keys: Vec<&String> = current_i18n.keys().collect(); sorted_keys.sort(); + + let current_i18n_entry_count = current_i18n.len(); + let mut uptodate_entries = 0; + let mut outdated_entries = 0; + let mut unused_entries = 0; + let mut notfound_entries = 0; + let mut unknown_entries = 0; + for key in sorted_keys { let state = current_i18n.get(key).unwrap(); if state.state != LocalizationState::UpToDate { + match state.state { + LocalizationState::Outdated => outdated_entries += 1, + LocalizationState::NotFound => notfound_entries += 1, + LocalizationState::Unknown => unknown_entries += 1, + LocalizationState::Unused => unused_entries += 1, + LocalizationState::UpToDate => unreachable!(), + }; + println!( "[{:9}] {:60}{:40} {:40}", format!("{:?}", state.state), @@ -260,6 +301,35 @@ fn test_all_localizations<'a>() { uptodate_entries += 1; } } - println!("{} entries are up-to-date\n", uptodate_entries); + + println!( + "\n{} up-to-date, {} outdated, {} unused, {} not found, {} unknown entries found", + uptodate_entries, outdated_entries, unused_entries, notfound_entries, unknown_entries + ); + + // Calculate key count that actually matter for the status of the translation + // Unused entries don't break the game + let real_entry_count = current_i18n_entry_count - unused_entries; + let uptodate_percent = (uptodate_entries as f32 / real_entry_count as f32) * 100_f32; + let outdated_percent = (outdated_entries as f32 / real_entry_count as f32) * 100_f32; + let untranslated_percent = + ((notfound_entries + unknown_entries) as f32 / real_entry_count as f32) * 100_f32; + + println!( + "{}% up-to-date, {}% outdated, {}% untranslated\n", + uptodate_percent, outdated_percent, untranslated_percent, + ); + + overall_uptodate_entry_count += uptodate_entries; + overall_outdated_entry_count += outdated_entries; + overall_untranslated_entry_count += notfound_entries + unknown_entries; + overall_real_entry_count += real_entry_count; } + + println!( + "Overall translation status: {}% up-to-date, {}% outdated, {}% untranslated\n", + (overall_uptodate_entry_count as f32 / overall_real_entry_count as f32) * 100_f32, + (overall_outdated_entry_count as f32 / overall_real_entry_count as f32) * 100_f32, + (overall_untranslated_entry_count as f32 / overall_real_entry_count as f32) * 100_f32, + ); } From 8e523364ac8b3a155938c98010562c664d577c57 Mon Sep 17 00:00:00 2001 From: Kai <6393786-Fexii@users.noreply.gitlab.com> Date: Sun, 12 Jul 2020 11:59:18 -0700 Subject: [PATCH 14/85] Prevent GameInputs from being bound to multiple keys unless explicitly allowed. Add a Reset to Defaults button for controls. --- assets/voxygen/i18n/en.ron | 2 + voxygen/src/hud/mod.rs | 5 + voxygen/src/hud/settings_window.rs | 142 +++++++++++++++++------------ voxygen/src/session.rs | 6 +- voxygen/src/settings.rs | 76 ++++----------- voxygen/src/window.rs | 79 ++++++++++++++++ 6 files changed, 194 insertions(+), 116 deletions(-) diff --git a/assets/voxygen/i18n/en.ron b/assets/voxygen/i18n/en.ron index 39c8a2dba3..91073a0a08 100644 --- a/assets/voxygen/i18n/en.ron +++ b/assets/voxygen/i18n/en.ron @@ -303,6 +303,8 @@ magically infused items?"#, "hud.settings.audio_device": "Audio Device", "hud.settings.awaitingkey": "Press a key...", + "hud.settings.unbound": "None", + "hud.settings.reset_keybinds": "Reset to Defaults", "hud.social": "Social", "hud.social.online": "Online", diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index da04cedf2f..5e4bc19d81 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -67,6 +67,7 @@ const TEXT_BG: Color = Color::Rgba(0.0, 0.0, 0.0, 1.0); const MENU_BG: Color = Color::Rgba(0.0, 0.0, 0.0, 0.4); //const TEXT_COLOR_2: Color = Color::Rgba(0.0, 0.0, 0.0, 1.0); const TEXT_COLOR_3: Color = Color::Rgba(1.0, 1.0, 1.0, 0.1); +const TEXT_BIND_CONFLICT_COLOR: Color = Color::Rgba(1.0, 0.0, 0.0, 1.0); const BLACK: Color = Color::Rgba(0.0, 0.0, 0.0, 1.0); //const BG_COLOR: Color = Color::Rgba(1.0, 1.0, 1.0, 0.8); const HP_COLOR: Color = Color::Rgba(0.33, 0.63, 0.0, 1.0); @@ -282,6 +283,7 @@ pub enum Event { Quit, ChangeLanguage(LanguageMetadata), ChangeBinding(GameInput), + ResetBindings, ChangeFreeLookBehavior(PressBehavior), ChangeAutoWalkBehavior(PressBehavior), ChangeStopAutoWalkOnInput(bool), @@ -1744,6 +1746,9 @@ impl Hud { settings_window::Event::ChangeBinding(game_input) => { events.push(Event::ChangeBinding(game_input)); }, + settings_window::Event::ResetBindings => { + events.push(Event::ResetBindings); + }, settings_window::Event::ChangeFreeLookBehavior(behavior) => { events.push(Event::ChangeFreeLookBehavior(behavior)); }, diff --git a/voxygen/src/hud/settings_window.rs b/voxygen/src/hud/settings_window.rs index 796f1a52fd..9c16b920be 100644 --- a/voxygen/src/hud/settings_window.rs +++ b/voxygen/src/hud/settings_window.rs @@ -1,6 +1,6 @@ use super::{ - img_ids::Imgs, BarNumbers, CrosshairType, PressBehavior, ShortcutNumbers, Show, XpBar, MENU_BG, - TEXT_COLOR, + img_ids::Imgs, BarNumbers, CrosshairType, PressBehavior, ShortcutNumbers, Show, XpBar, + ERROR_COLOR, MENU_BG, TEXT_BIND_CONFLICT_COLOR, TEXT_COLOR, }; use crate::{ i18n::{list_localizations, LanguageMetadata, VoxygenLocalization}, @@ -32,6 +32,7 @@ widget_ids! { settings_scrollbar, controls_texts[], controls_buttons[], + reset_controls_button, controls_alignment_rectangle, button_help, button_help2, @@ -250,6 +251,7 @@ pub enum Event { SpeechBubbleIcon(bool), ChangeLanguage(LanguageMetadata), ChangeBinding(GameInput), + ResetBindings, ChangeFreeLookBehavior(PressBehavior), ChangeAutoWalkBehavior(PressBehavior), ChangeStopAutoWalkOnInput(bool), @@ -1512,23 +1514,25 @@ impl<'a> Widget for SettingsWindow<'a> { // Contents if let SettingsTab::Controls = self.show.settings_tab { + // Used for sequential placement in a flow-down pattern + let mut previous_element_id = None; + let mut keybindings_vec: Vec = GameInput::iterator().collect(); + keybindings_vec.sort(); + let controls = &self.global_state.settings.controls; - if controls.keybindings.len() > state.ids.controls_texts.len() - || controls.keybindings.len() > state.ids.controls_buttons.len() + if keybindings_vec.len() > state.ids.controls_texts.len() + || keybindings_vec.len() > state.ids.controls_buttons.len() { state.update(|s| { s.ids .controls_texts - .resize(controls.keybindings.len(), &mut ui.widget_id_generator()); + .resize(keybindings_vec.len(), &mut ui.widget_id_generator()); s.ids .controls_buttons - .resize(controls.keybindings.len(), &mut ui.widget_id_generator()); + .resize(keybindings_vec.len(), &mut ui.widget_id_generator()); }); } - // Used for sequential placement in a flow-down pattern - let mut previous_text_id = None; - let mut keybindings_vec: Vec<&GameInput> = controls.keybindings.keys().collect(); - keybindings_vec.sort(); + // Loop all existing keybindings and the ids for text and button widgets for (game_input, (&text_id, &button_id)) in keybindings_vec.into_iter().zip( state @@ -1537,58 +1541,82 @@ impl<'a> Widget for SettingsWindow<'a> { .iter() .zip(state.ids.controls_buttons.iter()), ) { - if let Some(key) = controls.get_binding(*game_input) { - let loc_key = self - .localized_strings - .get(game_input.get_localization_key()); - let key_string = match self.global_state.window.remapping_keybindings { - Some(game_input_binding) => { - if *game_input == game_input_binding { - String::from(self.localized_strings.get("hud.settings.awaitingkey")) + let (key_string, key_color) = + if self.global_state.window.remapping_keybindings == Some(game_input) { + ( + String::from(self.localized_strings.get("hud.settings.awaitingkey")), + TEXT_COLOR, + ) + } else if let Some(key) = controls.get_binding(game_input) { + ( + key.to_string(), + if controls.has_conflicting_bindings(key) { + TEXT_BIND_CONFLICT_COLOR } else { - key.to_string() - } - }, - None => key.to_string(), + TEXT_COLOR + }, + ) + } else { + ( + String::from(self.localized_strings.get("hud.settings.unbound")), + ERROR_COLOR, + ) }; - - let text_widget = Text::new(loc_key) - .color(TEXT_COLOR) - .font_id(self.fonts.cyri.conrod_id) - .font_size(self.fonts.cyri.scale(18)); - let button_widget = Button::new() - .label(&key_string) - .label_color(TEXT_COLOR) - .label_font_id(self.fonts.cyri.conrod_id) - .label_font_size(self.fonts.cyri.scale(15)) - .w(150.0) - .rgba(0.0, 0.0, 0.0, 0.0) - .border_rgba(0.0, 0.0, 0.0, 255.0) - .label_y(Relative::Scalar(3.0)); - // Place top-left if it's the first text, else under the previous one - let text_widget = match previous_text_id { - None => text_widget.top_left_with_margins_on( - state.ids.settings_content, - 10.0, - 5.0, - ), - Some(prev_id) => text_widget.down_from(prev_id, 10.0), - }; - let text_width = text_widget.get_w(ui).unwrap_or(0.0); - text_widget.set(text_id, ui); - if button_widget - .right_from(text_id, 350.0 - text_width) - .set(button_id, ui) - .was_clicked() - { - events.push(Event::ChangeBinding(*game_input)); - } - // Set the previous id to the current one for the next cycle - previous_text_id = Some(text_id); + let loc_key = self + .localized_strings + .get(game_input.get_localization_key()); + let text_widget = Text::new(loc_key) + .color(TEXT_COLOR) + .font_id(self.fonts.cyri.conrod_id) + .font_size(self.fonts.cyri.scale(18)); + let button_widget = Button::new() + .label(&key_string) + .label_color(key_color) + .label_font_id(self.fonts.cyri.conrod_id) + .label_font_size(self.fonts.cyri.scale(15)) + .w(150.0) + .rgba(0.0, 0.0, 0.0, 0.0) + .border_rgba(0.0, 0.0, 0.0, 255.0) + .label_y(Relative::Scalar(3.0)); + // Place top-left if it's the first text, else under the previous one + let text_widget = match previous_element_id { + None => { + text_widget.top_left_with_margins_on(state.ids.settings_content, 10.0, 5.0) + }, + Some(prev_id) => text_widget.down_from(prev_id, 10.0), + }; + let text_width = text_widget.get_w(ui).unwrap_or(0.0); + text_widget.set(text_id, ui); + if button_widget + .right_from(text_id, 350.0 - text_width) + .set(button_id, ui) + .was_clicked() + { + events.push(Event::ChangeBinding(game_input)); } + // Set the previous id to the current one for the next cycle + previous_element_id = Some(text_id); + } + if let Some(prev_id) = previous_element_id { + let key_string = self.localized_strings.get("hud.settings.reset_keybinds"); + let button_widget = Button::new() + .label(&key_string) + .label_color(TEXT_COLOR) + .label_font_id(self.fonts.cyri.conrod_id) + .label_font_size(self.fonts.cyri.scale(18)) + .down_from(prev_id, 20.0) + .w(200.0) + .rgba(0.0, 0.0, 0.0, 0.0) + .border_rgba(0.0, 0.0, 0.0, 255.0) + .label_y(Relative::Scalar(3.0)) + .set(state.ids.reset_controls_button, ui); + if button_widget.was_clicked() { + events.push(Event::ResetBindings); + } + previous_element_id = Some(state.ids.reset_controls_button) } // Add an empty text widget to simulate some bottom margin, because conrod sucks - if let Some(prev_id) = previous_text_id { + if let Some(prev_id) = previous_element_id { Rectangle::fill_with([1.0, 1.0], color::TRANSPARENT) .down_from(prev_id, 10.0) .set(state.ids.controls_alignment_rectangle, ui); diff --git a/voxygen/src/session.rs b/voxygen/src/session.rs index b256174024..e3b3991592 100644 --- a/voxygen/src/session.rs +++ b/voxygen/src/session.rs @@ -6,7 +6,7 @@ use crate::{ key_state::KeyState, menu::char_selection::CharSelectionState, scene::{camera, Scene, SceneData}, - settings::AudioOutput, + settings::{AudioOutput, ControlSettings}, window::{AnalogGameInput, Event, GameInput}, Direction, Error, GlobalState, PlayState, PlayStateResult, }; @@ -922,6 +922,10 @@ impl PlayState for SessionState { HudEvent::ChangeBinding(game_input) => { global_state.window.set_keybinding_mode(game_input); }, + HudEvent::ResetBindings => { + global_state.settings.controls = ControlSettings::default(); + global_state.settings.save_to_file_warn(); + }, HudEvent::ChangeFreeLookBehavior(behavior) => { global_state.settings.gameplay.free_look_behavior = behavior; }, diff --git a/voxygen/src/settings.rs b/voxygen/src/settings.rs index 013953c65c..373515a957 100644 --- a/voxygen/src/settings.rs +++ b/voxygen/src/settings.rs @@ -100,9 +100,23 @@ impl ControlSettings { self.keybindings.insert(game_input, key_mouse); } + /// Return true if this key is used for multiple GameInputs that aren't + /// expected to be safe to have bound to the same key at the same time + pub fn has_conflicting_bindings(&self, key_mouse: KeyMouse) -> bool { + if let Some(game_inputs) = self.inverse_keybindings.get(&key_mouse) { + for a in game_inputs.iter() { + for b in game_inputs.iter() { + if !GameInput::can_share_bindings(*a, *b) { + return true; + } + } + } + } + false + } + pub fn default_binding(game_input: GameInput) -> KeyMouse { - // If a new GameInput is added, be sure to update ControlSettings::default() - // too! + // If a new GameInput is added, be sure to update GameInput::iterator() too! match game_input { GameInput::Primary => KeyMouse::Mouse(MouseButton::Left), GameInput::Secondary => KeyMouse::Mouse(MouseButton::Right), @@ -157,68 +171,14 @@ impl ControlSettings { } } } + impl Default for ControlSettings { fn default() -> Self { let mut new_settings = Self { keybindings: HashMap::new(), inverse_keybindings: HashMap::new(), }; - // Sets the initial keybindings for those GameInputs. If a new one is created in - // future, you'll have to update default_binding, and you should update this vec - // too. - let game_inputs = vec![ - GameInput::Primary, - GameInput::Secondary, - GameInput::ToggleCursor, - GameInput::MoveForward, - GameInput::MoveBack, - GameInput::MoveLeft, - GameInput::MoveRight, - GameInput::Jump, - GameInput::Sit, - GameInput::Dance, - GameInput::Glide, - GameInput::Climb, - GameInput::ClimbDown, - GameInput::Swim, - //GameInput::WallLeap, - GameInput::ToggleLantern, - GameInput::Mount, - GameInput::Enter, - GameInput::Command, - GameInput::Escape, - GameInput::Map, - GameInput::Bag, - GameInput::Social, - GameInput::Spellbook, - GameInput::Settings, - GameInput::ToggleInterface, - GameInput::Help, - GameInput::ToggleDebug, - GameInput::Fullscreen, - GameInput::Screenshot, - GameInput::ToggleIngameUi, - GameInput::Roll, - GameInput::Respawn, - GameInput::Interact, - GameInput::ToggleWield, - //GameInput::Charge, - GameInput::FreeLook, - GameInput::AutoWalk, - GameInput::CycleCamera, - GameInput::Slot1, - GameInput::Slot2, - GameInput::Slot3, - GameInput::Slot4, - GameInput::Slot5, - GameInput::Slot6, - GameInput::Slot7, - GameInput::Slot8, - GameInput::Slot9, - GameInput::Slot10, - GameInput::SwapLoadout, - ]; - for game_input in game_inputs { + for game_input in GameInput::iterator() { new_settings.insert_binding(game_input, ControlSettings::default_binding(game_input)); } new_settings diff --git a/voxygen/src/window.rs b/voxygen/src/window.rs index d7641cbc89..b3b08e3259 100644 --- a/voxygen/src/window.rs +++ b/voxygen/src/window.rs @@ -123,6 +123,85 @@ impl GameInput { GameInput::SwapLoadout => "gameinput.swaploadout", } } + + pub fn iterator() -> impl Iterator { + [ + GameInput::Primary, + GameInput::Secondary, + GameInput::ToggleCursor, + GameInput::MoveForward, + GameInput::MoveLeft, + GameInput::MoveRight, + GameInput::MoveBack, + GameInput::Jump, + GameInput::Sit, + GameInput::Dance, + GameInput::Glide, + GameInput::Climb, + GameInput::ClimbDown, + GameInput::Swim, + GameInput::ToggleLantern, + GameInput::Mount, + GameInput::Enter, + GameInput::Command, + GameInput::Escape, + GameInput::Map, + GameInput::Bag, + GameInput::Social, + GameInput::Spellbook, + GameInput::Settings, + GameInput::ToggleInterface, + GameInput::Help, + GameInput::ToggleDebug, + GameInput::Fullscreen, + GameInput::Screenshot, + GameInput::ToggleIngameUi, + GameInput::Roll, + GameInput::Respawn, + GameInput::Interact, + GameInput::ToggleWield, + GameInput::FreeLook, + GameInput::AutoWalk, + GameInput::Slot1, + GameInput::Slot2, + GameInput::Slot3, + GameInput::Slot4, + GameInput::Slot5, + GameInput::Slot6, + GameInput::Slot7, + GameInput::Slot8, + GameInput::Slot9, + GameInput::Slot10, + GameInput::SwapLoadout, + ] + .iter() + .copied() + } + + /// Return true if `a` and `b` are able to be bound to the same key at the + /// same time without conflict. For example, the player can't jump and climb + /// at the same time, so these can be bound to the same key. + pub fn can_share_bindings(a: GameInput, b: GameInput) -> bool { + a.get_representative_binding() == b.get_representative_binding() + } + + /// If two GameInputs are able to be bound at the same time, then they will + /// return the same value from this function (the representative value for + /// that set). This models the Find operation of a disjoint-set data + /// structure. + fn get_representative_binding(&self) -> GameInput { + match self { + GameInput::Jump => GameInput::Jump, + GameInput::Climb => GameInput::Jump, + GameInput::Swim => GameInput::Jump, + GameInput::Respawn => GameInput::Jump, + + GameInput::FreeLook => GameInput::FreeLook, + GameInput::AutoWalk => GameInput::FreeLook, + + _ => *self, + } + } } /// Represents a key that the game menus recognise after input mapping From 730a54dad5e715337bbb7958489cac0cae7c8ca5 Mon Sep 17 00:00:00 2001 From: Yusuf Bera Ertan Date: Mon, 13 Jul 2020 00:40:43 +0300 Subject: [PATCH 15/85] localization test: add per file data to overall status, better alignments --- voxygen/tests/check_i18n_files.rs | 72 +++++++++++++++++++++++-------- 1 file changed, 54 insertions(+), 18 deletions(-) diff --git a/voxygen/tests/check_i18n_files.rs b/voxygen/tests/check_i18n_files.rs index 54adba608d..0458e012df 100644 --- a/voxygen/tests/check_i18n_files.rs +++ b/voxygen/tests/check_i18n_files.rs @@ -99,6 +99,7 @@ fn generate_key_version<'a>( }; } + let mut error_check_set: Vec = vec![]; // Find commit for each keys repo.blame_file(path, None) .expect("Impossible to generate the Git blame") @@ -108,10 +109,13 @@ fn generate_key_version<'a>( let line = match state.key_line { Some(l) => l, None => { - eprintln!( - "Key {} does not have a git line in it's state! Skipping key.", - key - ); + if !error_check_set.contains(key) { + eprintln!( + "Key {} does not have a git line in it's state! Skipping key.", + key + ); + error_check_set.push(key.clone()); + } continue; }, }; @@ -171,18 +175,16 @@ fn test_all_localizations<'a>() { generate_key_version(&repo, &loc, &en_i18n_path, &i18n_en_blob); // Compare to other reference files - let mut overall_uptodate_entry_count = 0; - let mut overall_outdated_entry_count = 0; - let mut overall_untranslated_entry_count = 0; - let mut overall_real_entry_count = 0; - let i18n_files = i18n_files(&i18n_path); + let mut i18n_entry_counts: HashMap = HashMap::new(); for file in &i18n_files { let relfile = file.strip_prefix(&root_dir).unwrap(); if relfile == en_i18n_path { continue; } + println!("\n-----------------------------------"); println!("{:?}", relfile); + println!("-----------------------------------"); // Find the localization entry state let current_blob = read_file_from_path(&repo, &head_ref, &relfile); @@ -254,7 +256,7 @@ fn test_all_localizations<'a>() { // Display println!( - "{:10} {:60}{:40} {:40}", + "\n{:10} | {:60}| {:40} | {:40}\n", "State", "Key name", relfile.to_str().unwrap(), @@ -283,7 +285,7 @@ fn test_all_localizations<'a>() { }; println!( - "[{:9}] {:60}{:40} {:40}", + "[{:9}] | {:60}| {:40} | {:40}", format!("{:?}", state.state), key, state @@ -303,7 +305,7 @@ fn test_all_localizations<'a>() { } println!( - "\n{} up-to-date, {} outdated, {} unused, {} not found, {} unknown entries found", + "\n{} up-to-date, {} outdated, {} unused, {} not found, {} unknown entries", uptodate_entries, outdated_entries, unused_entries, notfound_entries, unknown_entries ); @@ -316,20 +318,54 @@ fn test_all_localizations<'a>() { ((notfound_entries + unknown_entries) as f32 / real_entry_count as f32) * 100_f32; println!( - "{}% up-to-date, {}% outdated, {}% untranslated\n", + "{:.2}% up-to-date, {:.2}% outdated, {:.2}% untranslated\n", uptodate_percent, outdated_percent, untranslated_percent, ); - overall_uptodate_entry_count += uptodate_entries; - overall_outdated_entry_count += outdated_entries; - overall_untranslated_entry_count += notfound_entries + unknown_entries; - overall_real_entry_count += real_entry_count; + i18n_entry_counts.insert( + file.clone(), + ( + uptodate_entries, + outdated_entries, + notfound_entries + unknown_entries, + real_entry_count, + ), + ); + } + + let mut overall_uptodate_entry_count = 0; + let mut overall_outdated_entry_count = 0; + let mut overall_untranslated_entry_count = 0; + let mut overall_real_entry_count = 0; + + println!("--------------------------------------------------------------------------------"); + println!("Overall Translation Status"); + println!("--------------------------------------------------------------------------------"); + println!( + "{:12}| {:8} | {:8} | {:8}", + "", "up-to-date", "outdated", "untranslated" + ); + + for (path, (uptodate, outdated, untranslated, real)) in i18n_entry_counts { + overall_uptodate_entry_count += uptodate; + overall_outdated_entry_count += outdated; + overall_untranslated_entry_count += untranslated; + overall_real_entry_count += real; + + println!( + "{:12}|{:8} |{:6} |{:8}", + path.file_name().unwrap().to_string_lossy(), + uptodate, + outdated, + untranslated + ); } println!( - "Overall translation status: {}% up-to-date, {}% outdated, {}% untranslated\n", + "\n{:.2}% up-to-date, {:.2}% outdated, {:.2}% untranslated", (overall_uptodate_entry_count as f32 / overall_real_entry_count as f32) * 100_f32, (overall_outdated_entry_count as f32 / overall_real_entry_count as f32) * 100_f32, (overall_untranslated_entry_count as f32 / overall_real_entry_count as f32) * 100_f32, ); + println!("--------------------------------------------------------------------------------\n"); } From 5d60fb2e1d34e6d6641634dd95f62805e13f18fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=A4rtens?= Date: Mon, 13 Jul 2020 09:50:55 +0200 Subject: [PATCH 16/85] move i18n test to i18n file to reduce compile time && merge gitlab jobs to only have 1 cargo test job --- .gitlab/CI/build.gitlab-ci.yml | 13 +- voxygen/src/i18n.rs | 382 ++++++++++++++++++++++++++++++ voxygen/tests/check_i18n_files.rs | 371 ----------------------------- 3 files changed, 385 insertions(+), 381 deletions(-) delete mode 100644 voxygen/tests/check_i18n_files.rs diff --git a/.gitlab/CI/build.gitlab-ci.yml b/.gitlab/CI/build.gitlab-ci.yml index f051c3dd47..1683d4c7b1 100644 --- a/.gitlab/CI/build.gitlab-ci.yml +++ b/.gitlab/CI/build.gitlab-ci.yml @@ -1,8 +1,11 @@ unittests: extends: .recompile-branch stage: build + variables: + GIT_DEPTH: 9999999999999 script: - ln -s /dockercache/cache-all target + - cargo test --package veloren-voxygen --lib test_all_localizations -- --nocapture --ignored - cargo test retry: max: 2 @@ -17,16 +20,6 @@ benchmarks: retry: max: 2 -localization-status: - extends: .recompile-branch - stage: build - variables: - GIT_DEPTH: 9999999999999 - allow_failure: true - script: - - ln -s /dockercache/cache-all target - - cargo test test_all_localizations -- --nocapture --ignored - # Coverage is needed on master for the README.md badge to work coverage: extends: .recompile diff --git a/voxygen/src/i18n.rs b/voxygen/src/i18n.rs index 57b6a376e7..9858ff74c0 100644 --- a/voxygen/src/i18n.rs +++ b/voxygen/src/i18n.rs @@ -175,3 +175,385 @@ pub fn list_localizations() -> Vec { /// Return the asset associated with the language_id pub fn i18n_asset_key(language_id: &str) -> String { "voxygen.i18n.".to_string() + language_id } + +#[cfg(test)] +mod tests { + use super::VoxygenLocalization; + use git2::Repository; + use ron::de::from_bytes; + use std::{ + collections::{HashMap, HashSet}, + fs, + path::{Path, PathBuf}, + }; + + /// List localization files as a PathBuf vector + fn i18n_files(i18n_dir: &Path) -> Vec { + fs::read_dir(i18n_dir) + .unwrap() + .map(|res| res.map(|e| e.path()).unwrap()) + .filter(|e| match e.extension() { + Some(ext) => ext == "ron", + None => false, + }) + .collect() + } + + #[derive(Debug, PartialEq)] + enum LocalizationState { + UpToDate, + NotFound, + Outdated, + Unknown, + Unused, + } + + #[derive(Debug)] + struct LocalizationEntryState { + pub key_line: Option, + pub chuck_line_range: Option<(usize, usize)>, + pub commit_id: Option, + pub state: LocalizationState, + } + + impl LocalizationEntryState { + pub fn new() -> LocalizationEntryState { + LocalizationEntryState { + key_line: None, + chuck_line_range: None, + commit_id: None, + state: LocalizationState::Unknown, + } + } + } + + /// Returns the Git blob associated with the given reference and path + #[allow(clippy::expect_fun_call)] // TODO: Pending review in #587 + fn read_file_from_path<'a>( + repo: &'a git2::Repository, + reference: &git2::Reference, + path: &std::path::Path, + ) -> git2::Blob<'a> { + let tree = reference + .peel_to_tree() + .expect("Impossible to peel HEAD to a tree object"); + tree.get_path(path) + .expect(&format!( + "Impossible to find the file {:?} in reference {:?}", + path, + reference.name() + )) + .to_object(&repo) + .unwrap() + .peel_to_blob() + .expect("Impossible to fetch the Git object") + } + + fn generate_key_version<'a>( + repo: &'a git2::Repository, + localization: &VoxygenLocalization, + path: &std::path::Path, + file_blob: &git2::Blob, + ) -> HashMap { + let mut keys: HashMap = localization + .string_map + .keys() + .map(|k| (k.to_owned(), LocalizationEntryState::new())) + .collect(); + let mut to_process: HashSet<&String> = localization.string_map.keys().collect(); + // Find key start lines + let file_content = std::str::from_utf8(file_blob.content()).expect("Got non UTF-8 file"); + for (line_nb, line) in file_content.lines().enumerate() { + let mut found_key = None; + + for key in to_process.iter() { + if line.contains(key.as_str()) { + found_key = Some(key.to_owned()); + break; + } + } + + if let Some(key) = found_key { + keys.get_mut(key).unwrap().key_line = Some(line_nb); + to_process.remove(&key); + }; + } + + let mut error_check_set: Vec = vec![]; + // Find commit for each keys + repo.blame_file(path, None) + .expect("Impossible to generate the Git blame") + .iter() + .for_each(|e: git2::BlameHunk| { + for (key, state) in keys.iter_mut() { + let line = match state.key_line { + Some(l) => l, + None => { + if !error_check_set.contains(key) { + eprintln!( + "Key {} does not have a git line in it's state! Skipping key.", + key + ); + error_check_set.push(key.clone()); + } + continue; + }, + }; + + if line >= e.final_start_line() + && line < e.final_start_line() + e.lines_in_hunk() + { + state.chuck_line_range = Some(( + e.final_start_line(), + e.final_start_line() + e.lines_in_hunk(), + )); + state.commit_id = match state.commit_id { + Some(existing_commit) => { + match repo.graph_descendant_of(e.final_commit_id(), existing_commit) + { + Ok(true) => Some(e.final_commit_id()), + Ok(false) => Some(existing_commit), + Err(err) => panic!(err), + } + }, + None => Some(e.final_commit_id()), + }; + } + } + }); + + keys + } + + #[test] + #[ignore] + #[allow(clippy::expect_fun_call)] + /// Test to verify all languages and print missing and faulty localisation + fn test_all_localizations() { + // Generate paths + let i18n_asset_path = Path::new("assets/voxygen/i18n/"); + let en_i18n_path = i18n_asset_path.join("en.ron"); + let root_dir = std::env::current_dir() + .map(|p| p.parent().expect("").to_owned()) + .unwrap(); + let i18n_path = root_dir.join(i18n_asset_path); + + if !root_dir.join(&en_i18n_path).is_file() { + panic!("Reference language file not found {:?}", &en_i18n_path) + } + + // Initialize Git objects + let repo = Repository::discover(&root_dir).expect(&format!( + "Failed to open the Git repository at {:?}", + &root_dir + )); + let head_ref = repo.head().expect("Impossible to get the HEAD reference"); + + // Read HEAD for the reference language file + let i18n_en_blob = read_file_from_path(&repo, &head_ref, &en_i18n_path); + let loc: VoxygenLocalization = from_bytes(i18n_en_blob.content()) + .expect("Expect to parse reference i18n RON file, can't proceed without it"); + let i18n_references: HashMap = + generate_key_version(&repo, &loc, &en_i18n_path, &i18n_en_blob); + + // Compare to other reference files + let i18n_files = i18n_files(&i18n_path); + let mut i18n_entry_counts: HashMap = HashMap::new(); + for file in &i18n_files { + let relfile = file.strip_prefix(&root_dir).unwrap(); + if relfile == en_i18n_path { + continue; + } + println!("\n-----------------------------------"); + println!("{:?}", relfile); + println!("-----------------------------------"); + + // Find the localization entry state + let current_blob = read_file_from_path(&repo, &head_ref, &relfile); + let current_loc: VoxygenLocalization = match from_bytes(current_blob.content()) { + Ok(v) => v, + Err(e) => { + eprintln!( + "Could not parse {} RON file, skipping: {}", + relfile.to_string_lossy(), + e + ); + continue; + }, + }; + let mut current_i18n = + generate_key_version(&repo, ¤t_loc, &relfile, ¤t_blob); + for (ref_key, ref_state) in i18n_references.iter() { + match current_i18n.get_mut(ref_key) { + Some(state) => { + let commit_id = match state.commit_id { + Some(c) => c, + None => { + eprintln!( + "Commit ID of key {} in i18n file {} is missing! Skipping key.", + ref_key, + relfile.to_string_lossy() + ); + continue; + }, + }; + let ref_commit_id = match ref_state.commit_id { + Some(c) => c, + None => { + eprintln!( + "Commit ID of key {} in reference i18n file is missing! \ + Skipping key.", + ref_key + ); + continue; + }, + }; + if commit_id != ref_commit_id + && !repo + .graph_descendant_of(commit_id, ref_commit_id) + .unwrap_or(false) + { + state.state = LocalizationState::Outdated; + } else { + state.state = LocalizationState::UpToDate; + } + }, + None => { + current_i18n.insert(ref_key.to_owned(), LocalizationEntryState { + key_line: None, + chuck_line_range: None, + commit_id: None, + state: LocalizationState::NotFound, + }); + }, + } + } + + let ref_keys: HashSet<&String> = i18n_references.keys().collect(); + for (_, state) in current_i18n + .iter_mut() + .filter(|&(k, _)| !ref_keys.contains(k)) + { + state.state = LocalizationState::Unused; + } + + // Display + println!( + "\n{:10} | {:60}| {:40} | {:40}\n", + "State", + "Key name", + relfile.to_str().unwrap(), + en_i18n_path.to_str().unwrap() + ); + + let mut sorted_keys: Vec<&String> = current_i18n.keys().collect(); + sorted_keys.sort(); + + let current_i18n_entry_count = current_i18n.len(); + let mut uptodate_entries = 0; + let mut outdated_entries = 0; + let mut unused_entries = 0; + let mut notfound_entries = 0; + let mut unknown_entries = 0; + + for key in sorted_keys { + let state = current_i18n.get(key).unwrap(); + if state.state != LocalizationState::UpToDate { + match state.state { + LocalizationState::Outdated => outdated_entries += 1, + LocalizationState::NotFound => notfound_entries += 1, + LocalizationState::Unknown => unknown_entries += 1, + LocalizationState::Unused => unused_entries += 1, + LocalizationState::UpToDate => unreachable!(), + }; + + println!( + "[{:9}] | {:60}| {:40} | {:40}", + format!("{:?}", state.state), + key, + state + .commit_id + .map(|s| format!("{}", s)) + .unwrap_or_else(|| "None".to_string()), + i18n_references + .get(key) + .map(|s| s.commit_id) + .flatten() + .map(|s| format!("{}", s)) + .unwrap_or_else(|| "None".to_string()), + ); + } else { + uptodate_entries += 1; + } + } + + println!( + "\n{} up-to-date, {} outdated, {} unused, {} not found, {} unknown entries", + uptodate_entries, + outdated_entries, + unused_entries, + notfound_entries, + unknown_entries + ); + + // Calculate key count that actually matter for the status of the translation + // Unused entries don't break the game + let real_entry_count = current_i18n_entry_count - unused_entries; + let uptodate_percent = (uptodate_entries as f32 / real_entry_count as f32) * 100_f32; + let outdated_percent = (outdated_entries as f32 / real_entry_count as f32) * 100_f32; + let untranslated_percent = + ((notfound_entries + unknown_entries) as f32 / real_entry_count as f32) * 100_f32; + + println!( + "{:.2}% up-to-date, {:.2}% outdated, {:.2}% untranslated\n", + uptodate_percent, outdated_percent, untranslated_percent, + ); + + i18n_entry_counts.insert( + file.clone(), + ( + uptodate_entries, + outdated_entries, + notfound_entries + unknown_entries, + real_entry_count, + ), + ); + } + + let mut overall_uptodate_entry_count = 0; + let mut overall_outdated_entry_count = 0; + let mut overall_untranslated_entry_count = 0; + let mut overall_real_entry_count = 0; + + println!("-----------------------------------------------------------------------------"); + println!("Overall Translation Status"); + println!("-----------------------------------------------------------------------------"); + println!( + "{:12}| {:8} | {:8} | {:8}", + "", "up-to-date", "outdated", "untranslated" + ); + + for (path, (uptodate, outdated, untranslated, real)) in i18n_entry_counts { + overall_uptodate_entry_count += uptodate; + overall_outdated_entry_count += outdated; + overall_untranslated_entry_count += untranslated; + overall_real_entry_count += real; + + println!( + "{:12}|{:8} |{:6} |{:8}", + path.file_name().unwrap().to_string_lossy(), + uptodate, + outdated, + untranslated + ); + } + + println!( + "\n{:.2}% up-to-date, {:.2}% outdated, {:.2}% untranslated", + (overall_uptodate_entry_count as f32 / overall_real_entry_count as f32) * 100_f32, + (overall_outdated_entry_count as f32 / overall_real_entry_count as f32) * 100_f32, + (overall_untranslated_entry_count as f32 / overall_real_entry_count as f32) * 100_f32, + ); + println!("-----------------------------------------------------------------------------\n"); + } +} diff --git a/voxygen/tests/check_i18n_files.rs b/voxygen/tests/check_i18n_files.rs deleted file mode 100644 index 0458e012df..0000000000 --- a/voxygen/tests/check_i18n_files.rs +++ /dev/null @@ -1,371 +0,0 @@ -use git2::Repository; -use ron::de::from_bytes; -use std::{ - collections::{HashMap, HashSet}, - fs, - path::{Path, PathBuf}, -}; -use veloren_voxygen::i18n::VoxygenLocalization; - -/// List localization files as a PathBuf vector -fn i18n_files(i18n_dir: &Path) -> Vec { - fs::read_dir(i18n_dir) - .unwrap() - .map(|res| res.map(|e| e.path()).unwrap()) - .filter(|e| match e.extension() { - Some(ext) => ext == "ron", - None => false, - }) - .collect() -} - -#[derive(Debug, PartialEq)] -enum LocalizationState { - UpToDate, - NotFound, - Outdated, - Unknown, - Unused, -} - -#[derive(Debug)] -struct LocalizationEntryState { - pub key_line: Option, - pub chuck_line_range: Option<(usize, usize)>, - pub commit_id: Option, - pub state: LocalizationState, -} - -impl LocalizationEntryState { - pub fn new() -> LocalizationEntryState { - LocalizationEntryState { - key_line: None, - chuck_line_range: None, - commit_id: None, - state: LocalizationState::Unknown, - } - } -} - -/// Returns the Git blob associated with the given reference and path -#[allow(clippy::expect_fun_call)] // TODO: Pending review in #587 -fn read_file_from_path<'a>( - repo: &'a git2::Repository, - reference: &git2::Reference, - path: &std::path::Path, -) -> git2::Blob<'a> { - let tree = reference - .peel_to_tree() - .expect("Impossible to peel HEAD to a tree object"); - tree.get_path(path) - .expect(&format!( - "Impossible to find the file {:?} in reference {:?}", - path, - reference.name() - )) - .to_object(&repo) - .unwrap() - .peel_to_blob() - .expect("Impossible to fetch the Git object") -} - -fn generate_key_version<'a>( - repo: &'a git2::Repository, - localization: &VoxygenLocalization, - path: &std::path::Path, - file_blob: &git2::Blob, -) -> HashMap { - let mut keys: HashMap = localization - .string_map - .keys() - .map(|k| (k.to_owned(), LocalizationEntryState::new())) - .collect(); - let mut to_process: HashSet<&String> = localization.string_map.keys().collect(); - // Find key start lines - let file_content = std::str::from_utf8(file_blob.content()).expect("Got non UTF-8 file"); - for (line_nb, line) in file_content.lines().enumerate() { - let mut found_key = None; - - for key in to_process.iter() { - if line.contains(key.as_str()) { - found_key = Some(key.to_owned()); - break; - } - } - - if let Some(key) = found_key { - keys.get_mut(key).unwrap().key_line = Some(line_nb); - to_process.remove(&key); - }; - } - - let mut error_check_set: Vec = vec![]; - // Find commit for each keys - repo.blame_file(path, None) - .expect("Impossible to generate the Git blame") - .iter() - .for_each(|e: git2::BlameHunk| { - for (key, state) in keys.iter_mut() { - let line = match state.key_line { - Some(l) => l, - None => { - if !error_check_set.contains(key) { - eprintln!( - "Key {} does not have a git line in it's state! Skipping key.", - key - ); - error_check_set.push(key.clone()); - } - continue; - }, - }; - - if line >= e.final_start_line() && line < e.final_start_line() + e.lines_in_hunk() { - state.chuck_line_range = Some(( - e.final_start_line(), - e.final_start_line() + e.lines_in_hunk(), - )); - state.commit_id = match state.commit_id { - Some(existing_commit) => { - match repo.graph_descendant_of(e.final_commit_id(), existing_commit) { - Ok(true) => Some(e.final_commit_id()), - Ok(false) => Some(existing_commit), - Err(err) => panic!(err), - } - }, - None => Some(e.final_commit_id()), - }; - } - } - }); - - keys -} - -#[test] -#[ignore] -#[allow(clippy::expect_fun_call)] // TODO: Pending review in #587 -#[allow(clippy::extra_unused_lifetimes)] // TODO: Pending review in #587 -#[allow(clippy::or_fun_call)] // TODO: Pending review in #587 -fn test_all_localizations<'a>() { - // Generate paths - let i18n_asset_path = Path::new("assets/voxygen/i18n/"); - let en_i18n_path = i18n_asset_path.join("en.ron"); - let root_dir = std::env::current_dir() - .map(|p| p.parent().expect("").to_owned()) - .unwrap(); - let i18n_path = root_dir.join(i18n_asset_path); - - if !root_dir.join(&en_i18n_path).is_file() { - panic!("Reference language file not found {:?}", &en_i18n_path) - } - - // Initialize Git objects - let repo = Repository::discover(&root_dir).expect(&format!( - "Failed to open the Git repository at {:?}", - &root_dir - )); - let head_ref = repo.head().expect("Impossible to get the HEAD reference"); - - // Read HEAD for the reference language file - let i18n_en_blob = read_file_from_path(&repo, &head_ref, &en_i18n_path); - let loc: VoxygenLocalization = from_bytes(i18n_en_blob.content()) - .expect("Expect to parse reference i18n RON file, can't proceed without it"); - let i18n_references: HashMap = - generate_key_version(&repo, &loc, &en_i18n_path, &i18n_en_blob); - - // Compare to other reference files - let i18n_files = i18n_files(&i18n_path); - let mut i18n_entry_counts: HashMap = HashMap::new(); - for file in &i18n_files { - let relfile = file.strip_prefix(&root_dir).unwrap(); - if relfile == en_i18n_path { - continue; - } - println!("\n-----------------------------------"); - println!("{:?}", relfile); - println!("-----------------------------------"); - - // Find the localization entry state - let current_blob = read_file_from_path(&repo, &head_ref, &relfile); - let current_loc: VoxygenLocalization = match from_bytes(current_blob.content()) { - Ok(v) => v, - Err(e) => { - eprintln!( - "Could not parse {} RON file, skipping: {}", - relfile.to_string_lossy(), - e - ); - continue; - }, - }; - let mut current_i18n = generate_key_version(&repo, ¤t_loc, &relfile, ¤t_blob); - for (ref_key, ref_state) in i18n_references.iter() { - match current_i18n.get_mut(ref_key) { - Some(state) => { - let commit_id = match state.commit_id { - Some(c) => c, - None => { - eprintln!( - "Commit ID of key {} in i18n file {} is missing! Skipping key.", - ref_key, - relfile.to_string_lossy() - ); - continue; - }, - }; - let ref_commit_id = match ref_state.commit_id { - Some(c) => c, - None => { - eprintln!( - "Commit ID of key {} in reference i18n file is missing! Skipping \ - key.", - ref_key - ); - continue; - }, - }; - if commit_id != ref_commit_id - && !repo - .graph_descendant_of(commit_id, ref_commit_id) - .unwrap_or(false) - { - state.state = LocalizationState::Outdated; - } else { - state.state = LocalizationState::UpToDate; - } - }, - None => { - current_i18n.insert(ref_key.to_owned(), LocalizationEntryState { - key_line: None, - chuck_line_range: None, - commit_id: None, - state: LocalizationState::NotFound, - }); - }, - } - } - - let ref_keys: HashSet<&String> = i18n_references.keys().collect(); - for (_, state) in current_i18n - .iter_mut() - .filter(|&(k, _)| !ref_keys.contains(k)) - { - state.state = LocalizationState::Unused; - } - - // Display - println!( - "\n{:10} | {:60}| {:40} | {:40}\n", - "State", - "Key name", - relfile.to_str().unwrap(), - en_i18n_path.to_str().unwrap() - ); - - let mut sorted_keys: Vec<&String> = current_i18n.keys().collect(); - sorted_keys.sort(); - - let current_i18n_entry_count = current_i18n.len(); - let mut uptodate_entries = 0; - let mut outdated_entries = 0; - let mut unused_entries = 0; - let mut notfound_entries = 0; - let mut unknown_entries = 0; - - for key in sorted_keys { - let state = current_i18n.get(key).unwrap(); - if state.state != LocalizationState::UpToDate { - match state.state { - LocalizationState::Outdated => outdated_entries += 1, - LocalizationState::NotFound => notfound_entries += 1, - LocalizationState::Unknown => unknown_entries += 1, - LocalizationState::Unused => unused_entries += 1, - LocalizationState::UpToDate => unreachable!(), - }; - - println!( - "[{:9}] | {:60}| {:40} | {:40}", - format!("{:?}", state.state), - key, - state - .commit_id - .map(|s| format!("{}", s)) - .unwrap_or("None".to_string()), - i18n_references - .get(key) - .map(|s| s.commit_id) - .flatten() - .map(|s| format!("{}", s)) - .unwrap_or("None".to_string()), - ); - } else { - uptodate_entries += 1; - } - } - - println!( - "\n{} up-to-date, {} outdated, {} unused, {} not found, {} unknown entries", - uptodate_entries, outdated_entries, unused_entries, notfound_entries, unknown_entries - ); - - // Calculate key count that actually matter for the status of the translation - // Unused entries don't break the game - let real_entry_count = current_i18n_entry_count - unused_entries; - let uptodate_percent = (uptodate_entries as f32 / real_entry_count as f32) * 100_f32; - let outdated_percent = (outdated_entries as f32 / real_entry_count as f32) * 100_f32; - let untranslated_percent = - ((notfound_entries + unknown_entries) as f32 / real_entry_count as f32) * 100_f32; - - println!( - "{:.2}% up-to-date, {:.2}% outdated, {:.2}% untranslated\n", - uptodate_percent, outdated_percent, untranslated_percent, - ); - - i18n_entry_counts.insert( - file.clone(), - ( - uptodate_entries, - outdated_entries, - notfound_entries + unknown_entries, - real_entry_count, - ), - ); - } - - let mut overall_uptodate_entry_count = 0; - let mut overall_outdated_entry_count = 0; - let mut overall_untranslated_entry_count = 0; - let mut overall_real_entry_count = 0; - - println!("--------------------------------------------------------------------------------"); - println!("Overall Translation Status"); - println!("--------------------------------------------------------------------------------"); - println!( - "{:12}| {:8} | {:8} | {:8}", - "", "up-to-date", "outdated", "untranslated" - ); - - for (path, (uptodate, outdated, untranslated, real)) in i18n_entry_counts { - overall_uptodate_entry_count += uptodate; - overall_outdated_entry_count += outdated; - overall_untranslated_entry_count += untranslated; - overall_real_entry_count += real; - - println!( - "{:12}|{:8} |{:6} |{:8}", - path.file_name().unwrap().to_string_lossy(), - uptodate, - outdated, - untranslated - ); - } - - println!( - "\n{:.2}% up-to-date, {:.2}% outdated, {:.2}% untranslated", - (overall_uptodate_entry_count as f32 / overall_real_entry_count as f32) * 100_f32, - (overall_outdated_entry_count as f32 / overall_real_entry_count as f32) * 100_f32, - (overall_untranslated_entry_count as f32 / overall_real_entry_count as f32) * 100_f32, - ); - println!("--------------------------------------------------------------------------------\n"); -} From 5f902b5eabbdab479df7ec9ed733b491e11ae79c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=A4rtens?= Date: Sun, 5 Jul 2020 22:51:07 +0200 Subject: [PATCH 17/85] doing a clean shutdown of the BParticipant once the TCP connection is suddenly interrupted --- network/src/participant.rs | 131 ++++++++++++++++++++++--------------- 1 file changed, 77 insertions(+), 54 deletions(-) diff --git a/network/src/participant.rs b/network/src/participant.rs index 28647b234d..07b908f5eb 100644 --- a/network/src/participant.rs +++ b/network/src/participant.rs @@ -11,6 +11,7 @@ use async_std::sync::RwLock; use futures::{ channel::{mpsc, oneshot}, future::FutureExt, + lock::Mutex, select, sink::SinkExt, stream::StreamExt, @@ -53,6 +54,12 @@ struct ControlChannels { s2b_shutdown_bparticipant_r: oneshot::Receiver>>, /* own */ } +//this is needed in case of a shutdown +struct BParticipantShutdown { + b2b_prios_flushed_r: oneshot::Receiver<()>, + mgr_to_shutdown: Vec>, +} + #[derive(Debug)] pub struct BParticipant { remote_pid: Pid, @@ -63,6 +70,7 @@ pub struct BParticipant { running_mgr: AtomicUsize, run_channels: Option, metrics: Arc, + shutdown_info: Mutex>, no_channel_error_info: RwLock<(Instant, u64)>, } @@ -106,6 +114,7 @@ impl BParticipant { run_channels, metrics, no_channel_error_info: RwLock::new((Instant::now(), 0)), + shutdown_info: Mutex::new(None), }, a2b_steam_open_s, b2a_stream_opened_r, @@ -125,6 +134,14 @@ impl BParticipant { let (w2b_frames_s, w2b_frames_r) = mpsc::unbounded::<(Cid, Frame)>(); let (prios, a2p_msg_s, b2p_notify_empty_stream_s) = PrioManager::new(self.metrics.clone(), self.remote_pid_string.clone()); + *self.shutdown_info.lock().await = Some(BParticipantShutdown { + b2b_prios_flushed_r, + mgr_to_shutdown: vec![ + shutdown_send_mgr_sender, + shutdown_open_mgr_sender, + shutdown_stream_close_mgr_sender, + ], + }); let run_channels = self.run_channels.take().unwrap(); futures::join!( @@ -152,15 +169,7 @@ impl BParticipant { shutdown_stream_close_mgr_receiver, b2p_notify_empty_stream_s, ), - self.participant_shutdown_mgr( - run_channels.s2b_shutdown_bparticipant_r, - b2b_prios_flushed_r, - vec!( - shutdown_send_mgr_sender, - shutdown_open_mgr_sender, - shutdown_stream_close_mgr_sender - ) - ), + self.participant_shutdown_mgr(run_channels.s2b_shutdown_bparticipant_r,), ); } @@ -248,6 +257,8 @@ impl BParticipant { "FIXME: the frame is actually drop. which is fine for now as the participant \ will be closed, but not if we do channel-takeover" ); + //TEMP FIX: as we dont have channel takeover yet drop the whole bParticipant + self.close_participant(2).await; false } else { true @@ -380,10 +391,10 @@ impl BParticipant { } } }, - Frame::Shutdown => error!( - "Somehow this Shutdown signal got here, i should probably handle it. To not \ - crash let me just put this message here" - ), + Frame::Shutdown => { + debug!("Shutdown received from remote side"); + self.close_participant(2).await; + }, f => unreachable!("never reaches frame!: {:?}", f), } } @@ -502,47 +513,11 @@ impl BParticipant { async fn participant_shutdown_mgr( &self, s2b_shutdown_bparticipant_r: oneshot::Receiver>>, - b2b_prios_flushed_r: oneshot::Receiver<()>, - mut to_shutdown: Vec>, ) { self.running_mgr.fetch_add(1, Ordering::Relaxed); trace!("start participant_shutdown_mgr"); let sender = s2b_shutdown_bparticipant_r.await.unwrap(); - debug!("closing all managers"); - for sender in to_shutdown.drain(..) { - if let Err(e) = sender.send(()) { - warn!(?e, "manager seems to be closed already, weird, maybe a bug"); - }; - } - debug!("closing all streams"); - for (sid, si) in self.streams.write().await.drain() { - trace!(?sid, "shutting down Stream"); - si.closed.store(true, Ordering::Relaxed); - } - debug!("waiting for prios to be flushed"); - b2b_prios_flushed_r.await.unwrap(); - debug!("closing all channels"); - for ci in self.channels.write().await.drain(..) { - if let Err(e) = ci.b2r_read_shutdown.send(()) { - debug!(?e, ?ci.cid, "seems like this read protocol got already dropped by closing the Stream itself, just ignoring the fact"); - }; - } - //Wait for other bparticipants mgr to close via AtomicUsize - const SLEEP_TIME: Duration = Duration::from_millis(5); - async_std::task::sleep(SLEEP_TIME).await; - let mut i: u32 = 1; - while self.running_mgr.load(Ordering::Relaxed) > 1 { - i += 1; - if i.rem_euclid(10) == 1 { - trace!( - "waiting for bparticipant mgr to shut down, remaining {}", - self.running_mgr.load(Ordering::Relaxed) - 1 - ); - } - async_std::task::sleep(SLEEP_TIME * i).await; - } - trace!("all bparticipant mgr (except me) are shut down now"); - self.metrics.participants_disconnected_total.inc(); + self.close_participant(1).await; sender.send(Ok(())).unwrap(); trace!("stop participant_shutdown_mgr"); self.running_mgr.fetch_sub(1, Ordering::Relaxed); @@ -638,9 +613,57 @@ impl BParticipant { ) } - /* - async fn close_participant(&self) { - + /// this will gracefully shut down the bparticipant + /// allowed_managers: the number of open managers to sleep on. Must be 1 for + /// shutdown_mgr and 2 if it comes from a send error. + async fn close_participant(&self, allowed_managers: usize) { + trace!("participant shutdown triggered"); + let mut info = match self.shutdown_info.lock().await.take() { + Some(info) => info, + None => { + error!( + "Close of participant seemed to be called twice, that's bad, ignoring the 2nd \ + close" + ); + return; + }, + }; + debug!("closing all managers"); + for sender in info.mgr_to_shutdown.drain(..) { + if let Err(e) = sender.send(()) { + warn!(?e, "manager seems to be closed already, weird, maybe a bug"); + }; + } + debug!("closing all streams"); + for (sid, si) in self.streams.write().await.drain() { + trace!(?sid, "shutting down Stream"); + si.closed.store(true, Ordering::Relaxed); + } + debug!("waiting for prios to be flushed"); + info.b2b_prios_flushed_r.await.unwrap(); + debug!("closing all channels"); + for ci in self.channels.write().await.drain(..) { + if let Err(e) = ci.b2r_read_shutdown.send(()) { + debug!(?e, ?ci.cid, "seems like this read protocol got already dropped by closing the Stream itself, just ignoring the fact"); + }; + } + //Wait for other bparticipants mgr to close via AtomicUsize + const SLEEP_TIME: Duration = Duration::from_millis(5); + async_std::task::sleep(SLEEP_TIME).await; + let mut i: u32 = 1; + while self.running_mgr.load(Ordering::Relaxed) > allowed_managers { + i += 1; + if i.rem_euclid(10) == 1 { + trace!( + ?allowed_managers, + "waiting for bparticipant mgr to shut down, remaining {}", + self.running_mgr.load(Ordering::Relaxed) - allowed_managers + ); + } + async_std::task::sleep(SLEEP_TIME * i).await; + } + trace!("all bparticipant mgr (except me) are shut down now"); + self.metrics.participants_disconnected_total.inc(); + debug!("bparticipant close done"); } - */ } From 4cefdcefea6534dbdf8401a23aacdb2606720986 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=A4rtens?= Date: Mon, 6 Jul 2020 00:13:53 +0200 Subject: [PATCH 18/85] zests fix - capitalize first letter --- network/examples/fileshare/src/commands.rs | 2 +- network/examples/fileshare/src/main.rs | 2 +- network/examples/fileshare/src/server.rs | 34 ++++---- network/examples/network-speed/src/main.rs | 16 ++-- network/examples/network-speed/src/metrics.rs | 4 +- network/src/api.rs | 56 ++++++------ network/src/channel.rs | 26 +++--- network/src/lib.rs | 2 +- network/src/metrics.rs | 38 ++++---- network/src/participant.rs | 86 +++++++++---------- network/src/prios.rs | 12 +-- network/src/protocols.rs | 26 +++--- network/src/scheduler.rs | 64 +++++++------- 13 files changed, 184 insertions(+), 184 deletions(-) diff --git a/network/examples/fileshare/src/commands.rs b/network/examples/fileshare/src/commands.rs index 99178ea018..3749f29c53 100644 --- a/network/examples/fileshare/src/commands.rs +++ b/network/examples/fileshare/src/commands.rs @@ -44,7 +44,7 @@ impl FileInfo { let mt = match fs::metadata(&path).await { Err(e) => { println!( - "cannot get metadata for file: {:?}, does it exist? Error: {:?}", + "Cannot get metadata for file: {:?}, does it exist? Error: {:?}", &path, &e ); return None; diff --git a/network/examples/fileshare/src/main.rs b/network/examples/fileshare/src/main.rs index 5647dfaf07..322afae3eb 100644 --- a/network/examples/fileshare/src/main.rs +++ b/network/examples/fileshare/src/main.rs @@ -70,7 +70,7 @@ fn file_exists(file: String) -> Result<(), String> { if file.exists() { Ok(()) } else { - Err(format!("file does not exist")) + Err(format!("File does not exist")) } } diff --git a/network/examples/fileshare/src/server.rs b/network/examples/fileshare/src/server.rs index f6312a58b1..c00cecd61d 100644 --- a/network/examples/fileshare/src/server.rs +++ b/network/examples/fileshare/src/server.rs @@ -54,12 +54,12 @@ impl Server { } async fn command_manager(&self, command_receiver: mpsc::UnboundedReceiver) { - trace!("start command_manager"); + trace!("Start command_manager"); command_receiver .for_each_concurrent(None, async move |cmd| { match cmd { LocalCommand::Shutdown => { - println!("shutting down service"); + println!("Shutting down service"); return; }, LocalCommand::Disconnect => { @@ -67,21 +67,21 @@ impl Server { for (_, p) in self.network.participants().await.drain() { self.network.disconnect(p).await.unwrap(); } - println!("disconnecting all connections"); + println!("Disconnecting all connections"); return; }, LocalCommand::Connect(addr) => { - println!("trying to connect to: {:?}", &addr); + println!("Trying to connect to: {:?}", &addr); match self.network.connect(addr.clone()).await { Ok(p) => self.loop_participant(p).await, Err(e) => { - println!("failled to connect to {:?}, err: {:?}", &addr, e); + println!("Failled to connect to {:?}, err: {:?}", &addr, e); }, } }, LocalCommand::Serve(fileinfo) => { self.served.write().await.push(fileinfo.clone()); - println!("serving file: {:?}", fileinfo.path); + println!("Serving file: {:?}", fileinfo.path); }, LocalCommand::List => { let mut total_file_infos = vec![]; @@ -110,11 +110,11 @@ impl Server { } }) .await; - trace!("stop command_manager"); + trace!("Stop command_manager"); } async fn connect_manager(&self) { - trace!("start connect_manager"); + trace!("Start connect_manager"); let iter = futures::stream::unfold((), |_| { self.network.connected().map(|r| r.ok().map(|v| (v, ()))) }); @@ -123,7 +123,7 @@ impl Server { self.loop_participant(participant).await; }) .await; - trace!("stop connect_manager"); + trace!("Stop connect_manager"); } async fn loop_participant(&self, p: Arc) { @@ -133,7 +133,7 @@ impl Server { p.opened().await, p.opened().await, ) { - debug!(?p, "connection successfully initiated"); + debug!(?p, "Connection successfully initiated"); let id = p.remote_pid(); let ri = Arc::new(Mutex::new(RemoteInfo::new(cmd_out, file_out, p))); self.remotes.write().await.insert(id, ri.clone()); @@ -146,24 +146,24 @@ impl Server { async fn handle_remote_cmd(&self, mut stream: Stream, remote_info: Arc>) { while let Ok(msg) = stream.recv::().await { - println!("got message: {:?}", &msg); + println!("Got message: {:?}", &msg); match msg { Command::List => { - info!("request to send my list"); + info!("Request to send my list"); let served = self.served.read().await.clone(); stream.send(served).unwrap(); }, Command::Get(id) => { for file_info in self.served.read().await.iter() { if file_info.id() == id { - info!("request to send file i got, sending it"); + info!("Request to send file i got, sending it"); if let Ok(data) = file_info.load().await { match remote_info.lock().await.file_out.send((file_info, data)) { Ok(_) => debug!("send file"), Err(e) => error!(?e, "sending file failed"), } } else { - warn!("cannot send file as loading failed, oes it still exist?"); + warn!("Cannot send file as loading failed, oes it still exist?"); } } } @@ -174,18 +174,18 @@ impl Server { async fn handle_files(&self, mut stream: Stream, _remote_info: Arc>) { while let Ok((fi, data)) = stream.recv::<(FileInfo, Vec)>().await { - debug!(?fi, "got file"); + debug!(?fi, "Got file"); let path = self.receiving_files.lock().await.remove(&fi.id()).flatten(); let path: PathBuf = match &path { Some(path) => shellexpand::tilde(&path).parse().unwrap(), None => { let mut path = std::env::current_dir().unwrap(); path.push(fi.path().file_name().unwrap()); - trace!("no path provided, saving down to {:?}", path); + trace!("No path provided, saving down to {:?}", path); PathBuf::from(path) }, }; - debug!("received file, going to save it under {:?}", path); + debug!("Received file, going to save it under {:?}", path); fs::write(path, data).await.unwrap(); } } diff --git a/network/examples/network-speed/src/main.rs b/network/examples/network-speed/src/main.rs index 3e702ae2ce..f7b8e3a033 100644 --- a/network/examples/network-speed/src/main.rs +++ b/network/examples/network-speed/src/main.rs @@ -98,7 +98,7 @@ fn main() { let address = match matches.value_of("protocol") { Some("tcp") => Address::Tcp(format!("{}:{}", ip, port).parse().unwrap()), Some("udp") => Address::Udp(format!("{}:{}", ip, port).parse().unwrap()), - _ => panic!("invalid mode, run --help!"), + _ => panic!("Invalid mode, run --help!"), }; let mut background = None; @@ -111,7 +111,7 @@ fn main() { thread::sleep(Duration::from_millis(200)); //start client after server client(address); }, - _ => panic!("invalid mode, run --help!"), + _ => panic!("Invalid mode, run --help!"), }; if let Some(background) = background { background.join().unwrap(); @@ -126,7 +126,7 @@ fn server(address: Address) { block_on(server.listen(address)).unwrap(); loop { - info!("waiting for participant to connect"); + info!("Waiting for participant to connect"); let p1 = block_on(server.connected()).unwrap(); //remote representation of p1 let mut s1 = block_on(p1.opened()).unwrap(); //remote representation of s1 block_on(async { @@ -138,10 +138,10 @@ fn server(address: Address) { let new = Instant::now(); let diff = new.duration_since(last); last = new; - println!("recv 1.000.000 took {}", diff.as_millis()); + println!("Recv 1.000.000 took {}", diff.as_millis()); } } - info!("other stream was closed"); + info!("Other stream was closed"); }); } } @@ -170,17 +170,17 @@ fn client(address: Address) { let new = Instant::now(); let diff = new.duration_since(last); last = new; - println!("send 1.000.000 took {}", diff.as_millis()); + println!("Send 1.000.000 took {}", diff.as_millis()); } if id > 2000000 { - println!("stop"); + println!("Stop"); std::thread::sleep(std::time::Duration::from_millis(5000)); break; } } drop(s1); std::thread::sleep(std::time::Duration::from_millis(5000)); - info!("closing participant"); + info!("Closing participant"); block_on(client.disconnect(p1)).unwrap(); std::thread::sleep(std::time::Duration::from_millis(25000)); info!("DROPPING! client"); diff --git a/network/examples/network-speed/src/metrics.rs b/network/examples/network-speed/src/metrics.rs index 978c686d58..4052c606a9 100644 --- a/network/examples/network-speed/src/metrics.rs +++ b/network/examples/network-speed/src/metrics.rs @@ -55,7 +55,7 @@ impl SimpleMetrics { Ok(Some(rq)) => rq, Ok(None) => continue, Err(e) => { - println!("error: {}", e); + println!("Error: {}", e); break; }, }; @@ -76,7 +76,7 @@ impl SimpleMetrics { _ => (), } } - debug!("stopping tiny_http server to serve metrics"); + debug!("Stopping tiny_http server to serve metrics"); })); Ok(()) } diff --git a/network/src/api.rs b/network/src/api.rs index 110bae0fa6..8a954c930b 100644 --- a/network/src/api.rs +++ b/network/src/api.rs @@ -198,7 +198,7 @@ impl Network { registry: Option<&Registry>, ) -> (Self, impl std::ops::FnOnce()) { let p = participant_id; - debug!(?p, "starting Network"); + debug!(?p, "Starting Network"); let (scheduler, listen_sender, connect_sender, connected_receiver, shutdown_sender) = Scheduler::new(participant_id, registry); ( @@ -211,13 +211,13 @@ impl Network { shutdown_sender: Some(shutdown_sender), }, move || { - trace!(?p, "starting sheduler in own thread"); + trace!(?p, "Starting sheduler in own thread"); let _handle = task::block_on( scheduler .run() .instrument(tracing::info_span!("scheduler", ?p)), ); - trace!(?p, "stopping sheduler and his own thread"); + trace!(?p, "Stopping sheduler and his own thread"); }, ) } @@ -309,7 +309,7 @@ impl Network { /// [`Addresses`]: crate::api::Address pub async fn connect(&self, address: Address) -> Result, NetworkError> { let (pid_sender, pid_receiver) = oneshot::channel::>(); - debug!(?address, "connect to address"); + debug!(?address, "Connect to address"); self.connect_sender .write() .await @@ -322,7 +322,7 @@ impl Network { let pid = participant.remote_pid; debug!( ?pid, - "received Participant id from remote and return to user" + "Received Participant id from remote and return to user" ); let participant = Arc::new(participant); self.participants @@ -427,21 +427,21 @@ impl Network { pub async fn disconnect(&self, participant: Arc) -> Result<(), NetworkError> { // Remove, Close and try_unwrap error when unwrap fails! let pid = participant.remote_pid; - debug!(?pid, "removing participant from network"); + debug!(?pid, "Removing participant from network"); self.participants.write().await.remove(&pid)?; participant.closed.store(true, Ordering::Relaxed); match Arc::try_unwrap(participant) { Err(_) => { warn!( - "you are disconnecting and still keeping a reference to this participant, \ + "You are disconnecting and still keeping a reference to this participant, \ this is a bad idea. Participant will only be dropped when you drop your last \ reference" ); Ok(()) }, Ok(mut participant) => { - trace!("waiting now for participant to close"); + trace!("Waiting now for participant to close"); let (finished_sender, finished_receiver) = oneshot::channel(); // we are deleting here asyncly before DROP is called. Because this is done // nativly async, while drop needs an BLOCK! Drop will recognis @@ -452,7 +452,7 @@ impl Network { .unwrap() .send((pid, finished_sender)) .await - .expect("something is wrong in internal scheduler coding"); + .expect("Something is wrong in internal scheduler coding"); match finished_receiver.await { Ok(Ok(())) => { trace!(?pid, "Participant is now closed"); @@ -618,13 +618,13 @@ impl Participant { // TODO: not sure if we can paralise that, check in future let mut stream_opened_receiver = self.b2a_stream_opened_r.write().await; if self.closed.load(Ordering::Relaxed) { - warn!(?self.remote_pid, "participant is closed but another open is tried on it"); + warn!(?self.remote_pid, "Participant is closed but another open is tried on it"); return Err(ParticipantError::ParticipantClosed); } match stream_opened_receiver.next().await { Some(stream) => { let sid = stream.sid; - debug!(?sid, ?self.remote_pid, "receive opened stream"); + debug!(?sid, ?self.remote_pid, "Receive opened stream"); Ok(stream) }, None => { @@ -840,10 +840,10 @@ impl Stream { impl Drop for Network { fn drop(&mut self) { let pid = self.local_pid; - debug!(?pid, "shutting down Network"); + debug!(?pid, "Shutting down Network"); debug!( ?pid, - "shutting down Participants of Network, while we still have metrics" + "Shutting down Participants of Network, while we still have metrics" ); task::block_on(async { // we need to carefully shut down here! as otherwise we might call @@ -855,20 +855,20 @@ impl Drop for Network { if let Err(e) = self.disconnect(p).await { error!( ?e, - "error while dropping network, the error occured when dropping a \ + "Error while dropping network, the error occured when dropping a \ participant but can't be notified to the user any more" ); } } self.participants.write().await.clear(); }); - debug!(?pid, "shutting down Scheduler"); + debug!(?pid, "Shutting down Scheduler"); self.shutdown_sender .take() .unwrap() .send(()) - .expect("scheduler is closed, but nobody other should be able to close it"); - debug!(?pid, "participants have shut down!"); + .expect("Scheduler is closed, but nobody other should be able to close it"); + debug!(?pid, "Participants have shut down!"); } } @@ -877,7 +877,7 @@ impl Drop for Participant { // ignore closed, as we need to send it even though we disconnected the // participant from network let pid = self.remote_pid; - debug!(?pid, "shutting down Participant"); + debug!(?pid, "Shutting down Participant"); match self.a2s_disconnect_s.take() { None => debug!( ?pid, @@ -886,14 +886,14 @@ impl Drop for Participant { Some(mut a2s_disconnect_s) => { debug!( ?pid, - "unclean shutdown detected, active waiting for client to be disconnected" + "Unclean shutdown detected, active waiting for client to be disconnected" ); task::block_on(async { let (finished_sender, finished_receiver) = oneshot::channel(); a2s_disconnect_s .send((self.remote_pid, finished_sender)) .await - .expect("something is wrong in internal scheduler coding"); + .expect("Something is wrong in internal scheduler coding"); match finished_receiver.await { Ok(Err(e)) => error!( ?pid, @@ -912,7 +912,7 @@ impl Drop for Participant { }); }, } - debug!(?pid, "network dropped"); + debug!(?pid, "Network dropped"); } } @@ -922,7 +922,7 @@ impl Drop for Stream { if !self.closed.load(Ordering::Relaxed) { let sid = self.sid; let pid = self.pid; - debug!(?pid, ?sid, "shutting down Stream"); + debug!(?pid, ?sid, "Shutting down Stream"); if task::block_on(self.a2b_close_stream_s.take().unwrap().send(self.sid)).is_err() { warn!( "Other side got already dropped, probably due to timing, other side will \ @@ -932,7 +932,7 @@ impl Drop for Stream { } else { let sid = self.sid; let pid = self.pid; - debug!(?pid, ?sid, "not needed"); + debug!(?pid, ?sid, "Drop not needed"); } } } @@ -1011,7 +1011,7 @@ impl core::fmt::Display for StreamError { impl core::fmt::Display for ParticipantError { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { - ParticipantError::ParticipantClosed => write!(f, "participant closed"), + ParticipantError::ParticipantClosed => write!(f, "Participant closed"), } } } @@ -1019,10 +1019,10 @@ impl core::fmt::Display for ParticipantError { impl core::fmt::Display for NetworkError { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { - NetworkError::NetworkClosed => write!(f, "network closed"), - NetworkError::ListenFailed(_) => write!(f, "listening failed"), - NetworkError::ConnectFailed(_) => write!(f, "connecting failed"), - NetworkError::GracefulDisconnectFailed(_) => write!(f, "graceful disconnect failed"), + NetworkError::NetworkClosed => write!(f, "Network closed"), + NetworkError::ListenFailed(_) => write!(f, "Listening failed"), + NetworkError::ConnectFailed(_) => write!(f, "Connecting failed"), + NetworkError::GracefulDisconnectFailed(_) => write!(f, "Graceful disconnect failed"), } } } diff --git a/network/src/channel.rs b/network/src/channel.rs index e7dac7134c..37ce5a4f8b 100644 --- a/network/src/channel.rs +++ b/network/src/channel.rs @@ -48,13 +48,13 @@ impl Channel { //reapply leftovers from handshake let cnt = leftover_cid_frame.len(); - trace!(?self.cid, ?cnt, "reapplying leftovers"); + trace!(?self.cid, ?cnt, "Reapplying leftovers"); for cid_frame in leftover_cid_frame.drain(..) { w2c_cid_frame_s.send(cid_frame).await.unwrap(); } - trace!(?self.cid, ?cnt, "all leftovers reapplied"); + trace!(?self.cid, ?cnt, "All leftovers reapplied"); - trace!(?self.cid, "start up channel"); + trace!(?self.cid, "Start up channel"); match protocol { Protocols::Tcp(tcp) => { futures::join!( @@ -70,7 +70,7 @@ impl Channel { }, } - trace!(?self.cid, "shut down channel"); + trace!(?self.cid, "Shut down channel"); } } @@ -178,20 +178,20 @@ impl Handshake { version, }, )) => { - trace!(?magic_number, ?version, "recv handshake"); + trace!(?magic_number, ?version, "Recv handshake"); self.metrics .frames_in_total .with_label_values(&["", &cid_string, "Handshake"]) .inc(); if magic_number != VELOREN_MAGIC_NUMBER { - error!(?magic_number, "connection with invalid magic_number"); + error!(?magic_number, "Connection with invalid magic_number"); #[cfg(debug_assertions)] { self.metrics .frames_out_total .with_label_values(&["", &cid_string, "Raw"]) .inc(); - debug!("sending client instructions before killing"); + debug!("Sending client instructions before killing"); c2w_frame_s .send(Frame::Raw(Self::WRONG_NUMBER.to_vec())) .await @@ -201,10 +201,10 @@ impl Handshake { return Err(()); } if version != VELOREN_NETWORK_VERSION { - error!(?version, "connection with wrong network version"); + error!(?version, "Connection with wrong network version"); #[cfg(debug_assertions)] { - debug!("sending client instructions before killing"); + debug!("Sending client instructions before killing"); self.metrics .frames_out_total .with_label_values(&["", &cid_string, "Raw"]) @@ -227,7 +227,7 @@ impl Handshake { } return Err(()); } - debug!("handshake completed"); + debug!("Handshake completed"); if self.init_handshake { self.send_init(&mut c2w_frame_s, &pid_string).await; } else { @@ -235,7 +235,7 @@ impl Handshake { } }, Some((_, Frame::Shutdown)) => { - info!("shutdown signal received"); + info!("Shutdown signal received"); self.metrics .frames_in_total .with_label_values(&[&pid_string, &cid_string, "Shutdown"]) @@ -277,11 +277,11 @@ impl Handshake { self.send_init(&mut c2w_frame_s, &pid_string).await; STREAM_ID_OFFSET2 }; - info!(?pid, "this Handshake is now configured!"); + info!(?pid, "This Handshake is now configured!"); Ok((pid, stream_id_offset, secret)) }, Some((_, Frame::Shutdown)) => { - info!("shutdown signal received"); + info!("Shutdown signal received"); self.metrics .frames_in_total .with_label_values(&[&pid_string, &cid_string, "Shutdown"]) diff --git a/network/src/lib.rs b/network/src/lib.rs index 36568f2dc7..eea927189c 100644 --- a/network/src/lib.rs +++ b/network/src/lib.rs @@ -67,7 +67,7 @@ //! let client = server_network.connected().await?; //! let mut stream = client.opened().await?; //! let msg: String = stream.recv().await?; -//! println!("got message: {}", msg); +//! println!("Got message: {}", msg); //! assert_eq!(msg, "Hello World"); //! Ok(()) //! } diff --git a/network/src/metrics.rs b/network/src/metrics.rs index cd809c72bc..4436a8ce7f 100644 --- a/network/src/metrics.rs +++ b/network/src/metrics.rs @@ -50,50 +50,50 @@ impl NetworkMetrics { let listen_requests_total = IntCounterVec::new( Opts::new( "listen_requests_total", - "shows the number of listen requests to the scheduler", + "Shows the number of listen requests to the scheduler", ), &["protocol"], )?; let connect_requests_total = IntCounterVec::new( Opts::new( "connect_requests_total", - "shows the number of connect requests to the scheduler", + "Shows the number of connect requests to the scheduler", ), &["protocol"], )?; let participants_connected_total = IntCounter::with_opts(Opts::new( "participants_connected_total", - "shows the number of participants connected to the network", + "Shows the number of participants connected to the network", ))?; let participants_disconnected_total = IntCounter::with_opts(Opts::new( "participants_disconnected_total", - "shows the number of participants disconnected to the network", + "Shows the number of participants disconnected to the network", ))?; let channels_connected_total = IntCounterVec::new( Opts::new( "channels_connected_total", - "number of all channels currently connected on the network", + "Number of all channels currently connected on the network", ), &["participant"], )?; let channels_disconnected_total = IntCounterVec::new( Opts::new( "channels_disconnected_total", - "number of all channels currently disconnected on the network", + "Number of all channels currently disconnected on the network", ), &["participant"], )?; let streams_opened_total = IntCounterVec::new( Opts::new( "streams_opened_total", - "number of all streams currently open on the network", + "Number of all streams currently open on the network", ), &["participant"], )?; let streams_closed_total = IntCounterVec::new( Opts::new( "streams_closed_total", - "number of all streams currently open on the network", + "Number of all streams currently open on the network", ), &["participant"], )?; @@ -112,42 +112,42 @@ impl NetworkMetrics { let frames_out_total = IntCounterVec::new( Opts::new( "frames_out_total", - "number of all frames send per channel, at the channel level", + "Number of all frames send per channel, at the channel level", ), &["participant", "channel", "frametype"], )?; let frames_in_total = IntCounterVec::new( Opts::new( "frames_in_total", - "number of all frames received per channel, at the channel level", + "Number of all frames received per channel, at the channel level", ), &["participant", "channel", "frametype"], )?; let frames_wire_out_total = IntCounterVec::new( Opts::new( "frames_wire_out_total", - "number of all frames send per channel, at the protocol level", + "Number of all frames send per channel, at the protocol level", ), &["channel", "frametype"], )?; let frames_wire_in_total = IntCounterVec::new( Opts::new( "frames_wire_in_total", - "number of all frames received per channel, at the protocol level", + "Number of all frames received per channel, at the protocol level", ), &["channel", "frametype"], )?; let wire_out_throughput = IntCounterVec::new( Opts::new( "wire_out_throughput", - "throupgput of all data frames send per channel, at the protocol level", + "Throupgput of all data frames send per channel, at the protocol level", ), &["channel"], )?; let wire_in_throughput = IntCounterVec::new( Opts::new( "wire_in_throughput", - "throupgput of all data frames send per channel, at the protocol level", + "Throupgput of all data frames send per channel, at the protocol level", ), &["channel"], )?; @@ -155,7 +155,7 @@ impl NetworkMetrics { let message_out_total = IntCounterVec::new( Opts::new( "message_out_total", - "number of messages send by streams on the network", + "Number of messages send by streams on the network", ), &["participant", "stream"], )?; @@ -163,28 +163,28 @@ impl NetworkMetrics { let message_out_throughput = IntCounterVec::new( Opts::new( "message_out_throughput", - "throughput of messages send by streams on the network", + "Throughput of messages send by streams on the network", ), &["participant", "stream"], )?; let queued_count = IntGaugeVec::new( Opts::new( "queued_count", - "queued number of messages by participant on the network", + "Queued number of messages by participant on the network", ), &["channel"], )?; let queued_bytes = IntGaugeVec::new( Opts::new( "queued_bytes", - "queued bytes of messages by participant on the network", + "Queued bytes of messages by participant on the network", ), &["channel"], )?; let participants_ping = IntGaugeVec::new( Opts::new( "participants_ping", - "ping time to participants on the network", + "Ping time to participants on the network", ), &["channel"], )?; diff --git a/network/src/participant.rs b/network/src/participant.rs index 07b908f5eb..7dac6102bf 100644 --- a/network/src/participant.rs +++ b/network/src/participant.rs @@ -187,7 +187,7 @@ impl BParticipant { const FRAMES_PER_TICK: usize = 10005; self.running_mgr.fetch_add(1, Ordering::Relaxed); let mut closing_up = false; - trace!("start send_mgr"); + trace!("Start send_mgr"); let mut send_cache = PidCidFrameCache::new(self.metrics.frames_out_total.clone(), self.remote_pid); //while !self.closed.load(Ordering::Relaxed) { @@ -196,7 +196,7 @@ impl BParticipant { prios.fill_frames(FRAMES_PER_TICK, &mut frames).await; let len = frames.len(); if len > 0 { - trace!("tick {}", len); + trace!("Tick {}", len); } for (_, frame) in frames { self.send_frame(frame, &mut send_cache).await; @@ -216,7 +216,7 @@ impl BParticipant { closing_up = true; } } - trace!("stop send_mgr"); + trace!("Stop send_mgr"); b2b_prios_flushed_s.send(()).unwrap(); self.running_mgr.fetch_sub(1, Ordering::Relaxed); } @@ -242,13 +242,13 @@ impl BParticipant { if let Err(e) = ci.b2w_frame_s.send(frame).await { warn!( ?e, - "the channel got closed unexpectedly, cleaning it up now." + "The channel got closed unexpectedly, cleaning it up now." ); let ci = lock.remove(0); if let Err(e) = ci.b2r_read_shutdown.send(()) { debug!( ?e, - "error shutdowning channel, which is prob fine as we detected it to no \ + "Error shutdowning channel, which is prob fine as we detected it to no \ longer work in the first place" ); }; @@ -270,7 +270,7 @@ impl BParticipant { guard.0 = now; let occurrences = guard.1 + 1; guard.1 = 0; - error!(?occurrences, "participant has no channel to communicate on"); + error!(?occurrences, "Participant has no channel to communicate on"); } else { guard.1 += 1; } @@ -286,7 +286,7 @@ impl BParticipant { a2p_msg_s: crossbeam_channel::Sender<(Prio, Sid, OutgoingMessage)>, ) { self.running_mgr.fetch_add(1, Ordering::Relaxed); - trace!("start handle_frames_mgr"); + trace!("Start handle_frames_mgr"); let mut messages = HashMap::new(); let mut dropped_instant = Instant::now(); let mut dropped_cnt = 0u64; @@ -310,7 +310,7 @@ impl BParticipant { .create_stream(sid, prio, promises, a2p_msg_s, &a2b_close_stream_s) .await; b2a_stream_opened_s.send(stream).await.unwrap(); - trace!("opened frame from remote"); + trace!("Opened frame from remote"); }, Frame::CloseStream { sid } => { // Closing is realised by setting a AtomicBool to true, however we also have a @@ -320,7 +320,7 @@ impl BParticipant { // be dropped... from remote, notify local trace!( ?sid, - "got remote request to close a stream, without flushing it, local \ + "Got remote request to close a stream, without flushing it, local \ messages are dropped" ); // no wait for flush here, as the remote wouldn't care anyway. @@ -330,11 +330,11 @@ impl BParticipant { .with_label_values(&[&self.remote_pid_string]) .inc(); si.closed.store(true, Ordering::Relaxed); - trace!(?sid, "closed stream from remote"); + trace!(?sid, "Closed stream from remote"); } else { warn!( ?sid, - "couldn't find stream to close, either this is a duplicate message, \ + "Couldn't find stream to close, either this is a duplicate message, \ or the local copy of the Stream got closed simultaniously" ); } @@ -367,7 +367,7 @@ impl BParticipant { warn!( ?e, ?mid, - "dropping message, as streams seem to be in act of beeing \ + "Dropping message, as streams seem to be in act of beeing \ dropped right now" ); } @@ -379,7 +379,7 @@ impl BParticipant { { warn!( ?dropped_cnt, - "dropping multiple messages as stream no longer seems to \ + "Dropping multiple messages as stream no longer seems to \ exist because it was dropped probably." ); dropped_cnt = 0; @@ -395,17 +395,17 @@ impl BParticipant { debug!("Shutdown received from remote side"); self.close_participant(2).await; }, - f => unreachable!("never reaches frame!: {:?}", f), + f => unreachable!("Frame should never reache participant!: {:?}", f), } } if dropped_cnt > 0 { warn!( ?dropped_cnt, - "dropping multiple messages as stream no longer seems to exist because it was \ + "Dropping multiple messages as stream no longer seems to exist because it was \ dropped probably." ); } - trace!("stop handle_frames_mgr"); + trace!("Stop handle_frames_mgr"); self.running_mgr.fetch_sub(1, Ordering::Relaxed); } @@ -422,7 +422,7 @@ impl BParticipant { w2b_frames_s: mpsc::UnboundedSender<(Cid, Frame)>, ) { self.running_mgr.fetch_add(1, Ordering::Relaxed); - trace!("start create_channel_mgr"); + trace!("Start create_channel_mgr"); s2b_create_channel_r .for_each_concurrent( None, @@ -444,7 +444,7 @@ impl BParticipant { .channels_connected_total .with_label_values(&[&self.remote_pid_string]) .inc(); - trace!(?cid, "running channel in participant"); + trace!(?cid, "Running channel in participant"); channel .run(protocol, w2b_frames_s, leftover_cid_frame) .await; @@ -452,12 +452,12 @@ impl BParticipant { .channels_disconnected_total .with_label_values(&[&self.remote_pid_string]) .inc(); - trace!(?cid, "channel got closed"); + trace!(?cid, "Channel got closed"); } }, ) .await; - trace!("stop create_channel_mgr"); + trace!("Stop create_channel_mgr"); self.running_mgr.fetch_sub(1, Ordering::Relaxed); } @@ -469,7 +469,7 @@ impl BParticipant { shutdown_open_mgr_receiver: oneshot::Receiver<()>, ) { self.running_mgr.fetch_add(1, Ordering::Relaxed); - trace!("start open_mgr"); + trace!("Start open_mgr"); let mut stream_ids = self.offset_sid; let mut send_cache = PidCidFrameCache::new(self.metrics.frames_out_total.clone(), self.remote_pid); @@ -479,7 +479,7 @@ impl BParticipant { next = a2b_steam_open_r.next().fuse() => next, _ = shutdown_open_mgr_receiver => None, } { - debug!(?prio, ?promises, "got request to open a new steam"); + debug!(?prio, ?promises, "Got request to open a new steam"); let a2p_msg_s = a2p_msg_s.clone(); let sid = stream_ids; let stream = self @@ -502,7 +502,7 @@ impl BParticipant { stream_ids += Sid::from(1); } } - trace!("stop open_mgr"); + trace!("Stop open_mgr"); self.running_mgr.fetch_sub(1, Ordering::Relaxed); } @@ -515,11 +515,11 @@ impl BParticipant { s2b_shutdown_bparticipant_r: oneshot::Receiver>>, ) { self.running_mgr.fetch_add(1, Ordering::Relaxed); - trace!("start participant_shutdown_mgr"); + trace!("Start participant_shutdown_mgr"); let sender = s2b_shutdown_bparticipant_r.await.unwrap(); self.close_participant(1).await; sender.send(Ok(())).unwrap(); - trace!("stop participant_shutdown_mgr"); + trace!("Stop participant_shutdown_mgr"); self.running_mgr.fetch_sub(1, Ordering::Relaxed); } @@ -530,7 +530,7 @@ impl BParticipant { b2p_notify_empty_stream_s: crossbeam_channel::Sender<(Sid, oneshot::Sender<()>)>, ) { self.running_mgr.fetch_add(1, Ordering::Relaxed); - trace!("start stream_close_mgr"); + trace!("Start stream_close_mgr"); let mut send_cache = PidCidFrameCache::new(self.metrics.frames_out_total.clone(), self.remote_pid); let mut shutdown_stream_close_mgr_receiver = shutdown_stream_close_mgr_receiver.fuse(); @@ -542,7 +542,7 @@ impl BParticipant { } { //TODO: make this concurrent! //TODO: Performance, closing is slow! - trace!(?sid, "got request from api to close steam"); + trace!(?sid, "Got request from api to close steam"); //This needs to first stop clients from sending any more. //Then it will wait for all pending messages (in prio) to be send to the // protocol After this happened the stream is closed @@ -550,24 +550,24 @@ impl BParticipant { // frame! If we would send it before, all followup messages couldn't // be handled at the remote side. - trace!(?sid, "stopping api to use this stream"); + trace!(?sid, "Stopping api to use this stream"); match self.streams.read().await.get(&sid) { Some(si) => { si.closed.store(true, Ordering::Relaxed); }, - None => warn!("couldn't find the stream, might be simulanious close from remote"), + None => warn!("Couldn't find the stream, might be simulanious close from remote"), } //TODO: what happens if RIGHT NOW the remote sends a StreamClose and this // streams get closed and removed? RACE CONDITION - trace!(?sid, "wait for stream to be flushed"); + trace!(?sid, "Wait for stream to be flushed"); let (s2b_stream_finished_closed_s, s2b_stream_finished_closed_r) = oneshot::channel(); b2p_notify_empty_stream_s .send((sid, s2b_stream_finished_closed_s)) .unwrap(); s2b_stream_finished_closed_r.await.unwrap(); - trace!(?sid, "stream was successfully flushed"); + trace!(?sid, "Stream was successfully flushed"); self.metrics .streams_closed_total .with_label_values(&[&self.remote_pid_string]) @@ -577,7 +577,7 @@ impl BParticipant { self.send_frame(Frame::CloseStream { sid }, &mut send_cache) .await; } - trace!("stop stream_close_mgr"); + trace!("Stop stream_close_mgr"); self.running_mgr.fetch_sub(1, Ordering::Relaxed); } @@ -617,7 +617,7 @@ impl BParticipant { /// allowed_managers: the number of open managers to sleep on. Must be 1 for /// shutdown_mgr and 2 if it comes from a send error. async fn close_participant(&self, allowed_managers: usize) { - trace!("participant shutdown triggered"); + trace!("Participant shutdown triggered"); let mut info = match self.shutdown_info.lock().await.take() { Some(info) => info, None => { @@ -628,23 +628,23 @@ impl BParticipant { return; }, }; - debug!("closing all managers"); + debug!("Closing all managers"); for sender in info.mgr_to_shutdown.drain(..) { if let Err(e) = sender.send(()) { - warn!(?e, "manager seems to be closed already, weird, maybe a bug"); + warn!(?e, "Manager seems to be closed already, weird, maybe a bug"); }; } - debug!("closing all streams"); + debug!("Closing all streams"); for (sid, si) in self.streams.write().await.drain() { - trace!(?sid, "shutting down Stream"); + trace!(?sid, "Shutting down Stream"); si.closed.store(true, Ordering::Relaxed); } - debug!("waiting for prios to be flushed"); + debug!("Waiting for prios to be flushed"); info.b2b_prios_flushed_r.await.unwrap(); - debug!("closing all channels"); + debug!("Closing all channels"); for ci in self.channels.write().await.drain(..) { if let Err(e) = ci.b2r_read_shutdown.send(()) { - debug!(?e, ?ci.cid, "seems like this read protocol got already dropped by closing the Stream itself, just ignoring the fact"); + debug!(?e, ?ci.cid, "Seems like this read protocol got already dropped by closing the Stream itself, just ignoring the fact"); }; } //Wait for other bparticipants mgr to close via AtomicUsize @@ -656,14 +656,14 @@ impl BParticipant { if i.rem_euclid(10) == 1 { trace!( ?allowed_managers, - "waiting for bparticipant mgr to shut down, remaining {}", + "Waiting for bparticipant mgr to shut down, remaining {}", self.running_mgr.load(Ordering::Relaxed) - allowed_managers ); } async_std::task::sleep(SLEEP_TIME * i).await; } - trace!("all bparticipant mgr (except me) are shut down now"); + trace!("All BParticipant mgr (except me) are shut down now"); self.metrics.participants_disconnected_total.inc(); - debug!("bparticipant close done"); + debug!("BParticipant close done"); } } diff --git a/network/src/prios.rs b/network/src/prios.rs index b3a06c4d0b..fbbad553e9 100644 --- a/network/src/prios.rs +++ b/network/src/prios.rs @@ -265,7 +265,7 @@ impl PrioManager { } //decrease pid_sid counter by 1 again let cnt = self.sid_owned.get_mut(&sid).expect( - "the pid_sid_owned counter works wrong, more pid,sid removed than \ + "The pid_sid_owned counter works wrong, more pid,sid removed than \ inserted", ); cnt.len -= 1; @@ -276,7 +276,7 @@ impl PrioManager { } } } else { - trace!(?msg.mid, "repush message"); + trace!(?msg.mid, "Repush message"); self.messages[prio as usize].push_front((sid, msg)); } }, @@ -358,28 +358,28 @@ mod tests { fn assert_header(frames: &mut VecDeque<(Sid, Frame)>, f_sid: u64, f_length: u64) { let frame = frames .pop_front() - .expect("frames vecdeque doesn't contain enough frames!") + .expect("Frames vecdeque doesn't contain enough frames!") .1; if let Frame::DataHeader { mid, sid, length } = frame { assert_eq!(mid, 1); assert_eq!(sid, Sid::new(f_sid)); assert_eq!(length, f_length); } else { - panic!("wrong frame type!, expected DataHeader"); + panic!("Wrong frame type!, expected DataHeader"); } } fn assert_data(frames: &mut VecDeque<(Sid, Frame)>, f_start: u64, f_data: Vec) { let frame = frames .pop_front() - .expect("frames vecdeque doesn't contain enough frames!") + .expect("Frames vecdeque doesn't contain enough frames!") .1; if let Frame::Data { mid, start, data } = frame { assert_eq!(mid, 1); assert_eq!(start, f_start); assert_eq!(data, f_data); } else { - panic!("wrong frame type!, expected Data"); + panic!("Wrong frame type!, expected Data"); } } diff --git a/network/src/protocols.rs b/network/src/protocols.rs index 3d918008c0..33f5b8a4b3 100644 --- a/network/src/protocols.rs +++ b/network/src/protocols.rs @@ -68,7 +68,7 @@ impl TcpProtocol { if let Err(e) = stream.read_exact(&mut bytes).await { warn!( ?e, - "closing tcp protocol due to read error, sending close frame to gracefully \ + "Closing tcp protocol due to read error, sending close frame to gracefully \ shutdown" ); w2c_cid_frame_s @@ -84,7 +84,7 @@ impl TcpProtocol { w2c_cid_frame_s: &mut mpsc::UnboundedSender<(Cid, Frame)>, end_receiver: oneshot::Receiver<()>, ) { - trace!("starting up tcp read()"); + trace!("Starting up tcp read()"); let mut metrics_cache = CidFrameCache::new(self.metrics.frames_wire_in_total.clone(), cid); let throughput_cache = self .metrics @@ -100,7 +100,7 @@ impl TcpProtocol { _ = end_receiver => break, }; if r.is_err() { - info!("tcp stream closed, shutting down read"); + info!("Tcp stream closed, shutting down read"); break; } let frame_no = bytes[0]; @@ -210,7 +210,7 @@ impl TcpProtocol { .await .expect("Channel or Participant seems no longer to exist"); } - trace!("shutting down tcp read()"); + trace!("Shutting down tcp read()"); } /// read_except and if it fails, close the protocol @@ -223,7 +223,7 @@ impl TcpProtocol { Err(e) => { warn!( ?e, - "got an error writing to tcp, going to close this channel" + "Got an error writing to tcp, going to close this channel" ); to_wire_receiver.close(); true @@ -236,7 +236,7 @@ impl TcpProtocol { // Limites Throughput per single Receiver but stays in same thread (maybe as its // in a threadpool) for TCP, UDP and MPSC pub async fn write_to_wire(&self, cid: Cid, mut c2w_frame_r: mpsc::UnboundedReceiver) { - trace!("starting up tcp write()"); + trace!("Starting up tcp write()"); let mut stream = self.stream.clone(); let mut metrics_cache = CidFrameCache::new(self.metrics.frames_wire_out_total.clone(), cid); let throughput_cache = self @@ -404,7 +404,7 @@ impl UdpProtocol { w2c_cid_frame_s: &mut mpsc::UnboundedSender<(Cid, Frame)>, end_receiver: oneshot::Receiver<()>, ) { - trace!("starting up udp read()"); + trace!("Starting up udp read()"); let mut metrics_cache = CidFrameCache::new(self.metrics.frames_wire_in_total.clone(), cid); let throughput_cache = self .metrics @@ -416,7 +416,7 @@ impl UdpProtocol { r = data_in.next().fuse() => r, _ = end_receiver => None, } { - trace!("got raw UDP message with len: {}", bytes.len()); + trace!("Got raw UDP message with len: {}", bytes.len()); let frame_no = bytes[0]; let frame = match frame_no { FRAME_HANDSHAKE => { @@ -511,11 +511,11 @@ impl UdpProtocol { metrics_cache.with_label_values(&frame).inc(); w2c_cid_frame_s.send((cid, frame)).await.unwrap(); } - trace!("shutting down udp read()"); + trace!("Shutting down udp read()"); } pub async fn write_to_wire(&self, cid: Cid, mut c2w_frame_r: mpsc::UnboundedReceiver) { - trace!("starting up udp write()"); + trace!("Starting up udp write()"); let mut buffer = [0u8; 2000]; let mut metrics_cache = CidFrameCache::new(self.metrics.frames_wire_out_total.clone(), cid); let throughput_cache = self @@ -588,7 +588,7 @@ impl UdpProtocol { }; let mut start = 0; while start < len { - trace!(?start, ?len, "splitting up udp frame in multiple packages"); + trace!(?start, ?len, "Splitting up udp frame in multiple packages"); match self .socket .send_to(&buffer[start..len], self.remote_addr) @@ -603,10 +603,10 @@ impl UdpProtocol { ); } }, - Err(e) => error!(?e, "need to handle that error!"), + Err(e) => error!(?e, "Need to handle that error!"), } } } - trace!("shutting down udp write()"); + trace!("Shutting down udp write()"); } } diff --git a/network/src/scheduler.rs b/network/src/scheduler.rs index 31cd0bd7ba..5264a89244 100644 --- a/network/src/scheduler.rs +++ b/network/src/scheduler.rs @@ -158,13 +158,13 @@ impl Scheduler { &self, a2s_listen_r: mpsc::UnboundedReceiver<(Address, oneshot::Sender>)>, ) { - trace!("start listen_mgr"); + trace!("Start listen_mgr"); a2s_listen_r .for_each_concurrent(None, |(address, s2a_listen_result_s)| { let address = address; async move { - debug!(?address, "got request to open a channel_creator"); + debug!(?address, "Got request to open a channel_creator"); self.metrics .listen_requests_total .with_label_values(&[match address { @@ -183,7 +183,7 @@ impl Scheduler { } }) .await; - trace!("stop listen_mgr"); + trace!("Stop listen_mgr"); } async fn connect_mgr( @@ -193,7 +193,7 @@ impl Scheduler { oneshot::Sender>, )>, ) { - trace!("start connect_mgr"); + trace!("Start connect_mgr"); while let Some((addr, pid_sender)) = a2s_connect_r.next().await { let (protocol, handshake) = match addr { Address::Tcp(addr) => { @@ -249,7 +249,7 @@ impl Scheduler { self.init_protocol(protocol, Some(pid_sender), handshake) .await; } - trace!("stop connect_mgr"); + trace!("Stop connect_mgr"); } async fn disconnect_mgr( @@ -259,14 +259,14 @@ impl Scheduler { oneshot::Sender>, )>, ) { - trace!("start disconnect_mgr"); + trace!("Start disconnect_mgr"); while let Some((pid, return_once_successful_shutdown)) = a2s_disconnect_r.next().await { //Closing Participants is done the following way: // 1. We drop our senders and receivers // 2. we need to close BParticipant, this will drop its senderns and receivers // 3. Participant will try to access the BParticipant senders and receivers with // their next api action, it will fail and be closed then. - trace!(?pid, "got request to close participant"); + trace!(?pid, "Got request to close participant"); if let Some(mut pi) = self.participants.write().await.remove(&pid) { let (finished_sender, finished_receiver) = oneshot::channel(); pi.s2b_shutdown_bparticipant_s @@ -278,36 +278,36 @@ impl Scheduler { let e = finished_receiver.await.unwrap(); return_once_successful_shutdown.send(e).unwrap(); } else { - debug!(?pid, "looks like participant is already dropped"); + debug!(?pid, "Looks like participant is already dropped"); return_once_successful_shutdown.send(Ok(())).unwrap(); } - trace!(?pid, "closed participant"); + trace!(?pid, "Closed participant"); } - trace!("stop disconnect_mgr"); + trace!("Stop disconnect_mgr"); } async fn prio_adj_mgr( &self, mut b2s_prio_statistic_r: mpsc::UnboundedReceiver<(Pid, u64, u64)>, ) { - trace!("start prio_adj_mgr"); + trace!("Start prio_adj_mgr"); while let Some((_pid, _frame_cnt, _unused)) = b2s_prio_statistic_r.next().await { //TODO adjust prios in participants here! } - trace!("stop prio_adj_mgr"); + trace!("Stop prio_adj_mgr"); } async fn scheduler_shutdown_mgr(&self, a2s_scheduler_shutdown_r: oneshot::Receiver<()>) { - trace!("start scheduler_shutdown_mgr"); + trace!("Start scheduler_shutdown_mgr"); a2s_scheduler_shutdown_r.await.unwrap(); self.closed.store(true, Ordering::Relaxed); - debug!("shutting down all BParticipants gracefully"); + debug!("Shutting down all BParticipants gracefully"); let mut participants = self.participants.write().await; let waitings = participants .drain() .map(|(pid, mut pi)| { - trace!(?pid, "shutting down BParticipants"); + trace!(?pid, "Shutting down BParticipants"); let (finished_sender, finished_receiver) = oneshot::channel(); pi.s2b_shutdown_bparticipant_s .take() @@ -317,13 +317,13 @@ impl Scheduler { (pid, finished_receiver) }) .collect::>(); - debug!("wait for partiticipants to be shut down"); + debug!("Wait for partiticipants to be shut down"); for (pid, recv) in waitings { if let Err(e) = recv.await { error!( ?pid, ?e, - "failed to finish sending all remainding messages to participant when \ + "Failed to finish sending all remainding messages to participant when \ shutting down" ); }; @@ -332,7 +332,7 @@ impl Scheduler { // some mgr: self.participant_channels.lock().await.take(); - trace!("stop scheduler_shutdown_mgr"); + trace!("Stop scheduler_shutdown_mgr"); } async fn channel_creator( @@ -341,7 +341,7 @@ impl Scheduler { s2s_stop_listening_r: oneshot::Receiver<()>, s2a_listen_result_s: oneshot::Sender>, ) { - trace!(?addr, "start up channel creator"); + trace!(?addr, "Start up channel creator"); match addr { Address::Tcp(addr) => { let listener = match net::TcpListener::bind(addr).await { @@ -353,13 +353,13 @@ impl Scheduler { info!( ?addr, ?e, - "listener couldn't be started due to error on tcp bind" + "Listener couldn't be started due to error on tcp bind" ); s2a_listen_result_s.send(Err(e)).unwrap(); return; }, }; - trace!(?addr, "listener bound"); + trace!(?addr, "Listener bound"); let mut incoming = listener.incoming(); let mut end_receiver = s2s_stop_listening_r.fuse(); while let Some(stream) = select! { @@ -383,13 +383,13 @@ impl Scheduler { info!( ?addr, ?e, - "listener couldn't be started due to error on udp bind" + "Listener couldn't be started due to error on udp bind" ); s2a_listen_result_s.send(Err(e)).unwrap(); return; }, }; - trace!(?addr, "listener bound"); + trace!(?addr, "Listener bound"); // receiving is done from here and will be piped to protocol as UDP does not // have any state let mut listeners = HashMap::new(); @@ -424,7 +424,7 @@ impl Scheduler { }, _ => unimplemented!(), } - trace!(?addr, "ending channel creator"); + trace!(?addr, "Ending channel creator"); } async fn udp_single_channel_connect( @@ -432,7 +432,7 @@ impl Scheduler { mut w2p_udp_package_s: mpsc::UnboundedSender>, ) { let addr = socket.local_addr(); - trace!(?addr, "start udp_single_channel_connect"); + trace!(?addr, "Start udp_single_channel_connect"); //TODO: implement real closing let (_end_sender, end_receiver) = oneshot::channel::<()>(); @@ -448,7 +448,7 @@ impl Scheduler { datavec.extend_from_slice(&data[0..size]); w2p_udp_package_s.send(datavec).await.unwrap(); } - trace!(?addr, "stop udp_single_channel_connect"); + trace!(?addr, "Stop udp_single_channel_connect"); } async fn init_protocol( @@ -477,7 +477,7 @@ impl Scheduler { // this is necessary for UDP to work at all and to remove code duplication self.pool.spawn_ok( async move { - trace!(?cid, "open channel and be ready for Handshake"); + trace!(?cid, "Open channel and be ready for Handshake"); let handshake = Handshake::new( cid, local_pid, @@ -490,11 +490,11 @@ impl Scheduler { trace!( ?cid, ?pid, - "detected that my channel is ready!, activating it :)" + "Detected that my channel is ready!, activating it :)" ); let mut participants = participants.write().await; if !participants.contains_key(&pid) { - debug!(?cid, "new participant connected via a channel"); + debug!(?cid, "New participant connected via a channel"); let ( bparticipant, a2b_steam_open_s, @@ -557,7 +557,7 @@ impl Scheduler { ?secret, "Detected incompatible Secret!, this is probably an attack!" ); - error!("just dropping here, TODO handle this correctly!"); + error!("Just dropping here, TODO handle this correctly!"); //TODO if let Some(pid_oneshot) = s2a_return_pid_s { // someone is waiting with `connect`, so give them their Error @@ -571,7 +571,7 @@ impl Scheduler { return; } error!( - "ufff i cant answer the pid_oneshot. as i need to create the SAME \ + "Ufff i cant answer the pid_oneshot. as i need to create the SAME \ participant. maybe switch to ARC" ); } @@ -584,7 +584,7 @@ impl Scheduler { pid_oneshot .send(Err(std::io::Error::new( std::io::ErrorKind::PermissionDenied, - "handshake failed, denying connection", + "Handshake failed, denying connection", ))) .unwrap(); } From 8fb445b0e84b133aeb1c4c862e3094f2b4fd2fdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=A4rtens?= Date: Mon, 6 Jul 2020 00:32:38 +0200 Subject: [PATCH 19/85] better lz4 --- network/src/message.rs | 21 +++++++++++---------- network/src/protocols.rs | 10 ++++------ network/src/types.rs | 2 +- 3 files changed, 16 insertions(+), 17 deletions(-) diff --git a/network/src/message.rs b/network/src/message.rs index d1e86f01ec..24b55bb21e 100644 --- a/network/src/message.rs +++ b/network/src/message.rs @@ -36,13 +36,16 @@ pub(crate) struct IncomingMessage { pub(crate) fn serialize(message: &M) -> MessageBuffer { //this will never fail: https://docs.rs/bincode/0.8.0/bincode/fn.serialize.html let writer = bincode::serialize(message).unwrap(); - MessageBuffer { data: writer } + MessageBuffer { + data: lz4_compress::compress(&writer), + } } //pub(crate) fn deserialize(buffer: MessageBuffer) -> // std::Result> { pub(crate) fn deserialize(buffer: MessageBuffer) -> bincode::Result { - let span = buffer.data; + let span = lz4_compress::decompress(&buffer.data) + .expect("lz4_compression error, failed to deserialze"); //this might fail if you choose the wrong type for M. in that case probably X // got transfered while you assume Y. probably this means your application // logic is wrong. E.g. You expect a String, but just get a u8. @@ -134,13 +137,11 @@ mod tests { fn serialize_test() { let msg = "abc"; let mb = serialize(&msg); - assert_eq!(mb.data.len(), 11); - assert_eq!(mb.data[0], 3); - assert_eq!(mb.data[1], 0); - assert_eq!(mb.data[7], 0); - assert_eq!(mb.data[8], b'a'); - assert_eq!(mb.data[8], 97); - assert_eq!(mb.data[9], b'b'); - assert_eq!(mb.data[10], b'c'); + assert_eq!(mb.data.len(), 9); + assert_eq!(mb.data[0], 34); + assert_eq!(mb.data[1], 3); + assert_eq!(mb.data[6], b'a'); + assert_eq!(mb.data[7], b'b'); + assert_eq!(mb.data[8], b'c'); } } diff --git a/network/src/protocols.rs b/network/src/protocols.rs index 33f5b8a4b3..801e227d85 100644 --- a/network/src/protocols.rs +++ b/network/src/protocols.rs @@ -182,10 +182,9 @@ impl TcpProtocol { bytes[15], ]); let length = u16::from_le_bytes([bytes[16], bytes[17]]); - let mut cdata = vec![0; length as usize]; + let mut data = vec![0; length as usize]; throughput_cache.inc_by(length as i64); - Self::read_except_or_close(cid, &stream, &mut cdata, w2c_cid_frame_s).await; - let data = lz4_compress::decompress(&cdata).unwrap(); + Self::read_except_or_close(cid, &stream, &mut data, w2c_cid_frame_s).await; Frame::Data { mid, start, data } }, FRAME_RAW => { @@ -348,7 +347,6 @@ impl TcpProtocol { }, Frame::Data { mid, start, data } => { throughput_cache.inc_by(data.len() as i64); - let cdata = lz4_compress::compress(&data); Self::write_or_close(&mut stream, &FRAME_DATA.to_be_bytes(), &mut c2w_frame_r) .await || Self::write_or_close(&mut stream, &mid.to_le_bytes(), &mut c2w_frame_r) @@ -357,11 +355,11 @@ impl TcpProtocol { .await || Self::write_or_close( &mut stream, - &(cdata.len() as u16).to_le_bytes(), + &(data.len() as u16).to_le_bytes(), &mut c2w_frame_r, ) .await - || Self::write_or_close(&mut stream, &cdata, &mut c2w_frame_r).await + || Self::write_or_close(&mut stream, &data, &mut c2w_frame_r).await }, Frame::Raw(data) => { Self::write_or_close(&mut stream, &FRAME_RAW.to_be_bytes(), &mut c2w_frame_r) diff --git a/network/src/types.rs b/network/src/types.rs index 46205f25ce..6b358cad54 100644 --- a/network/src/types.rs +++ b/network/src/types.rs @@ -34,7 +34,7 @@ pub const PROMISES_COMPRESSED: Promises = 8; pub const PROMISES_ENCRYPTED: Promises = 16; pub(crate) const VELOREN_MAGIC_NUMBER: [u8; 7] = [86, 69, 76, 79, 82, 69, 78]; //VELOREN -pub const VELOREN_NETWORK_VERSION: [u32; 3] = [0, 3, 0]; +pub const VELOREN_NETWORK_VERSION: [u32; 3] = [0, 4, 0]; pub(crate) const STREAM_ID_OFFSET1: Sid = Sid::new(0); pub(crate) const STREAM_ID_OFFSET2: Sid = Sid::new(u64::MAX / 2); From 9ae1d8474feaad87be38e3371890db7fb3f262e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=A4rtens?= Date: Mon, 6 Jul 2020 01:29:28 +0200 Subject: [PATCH 20/85] dropping participant on client disconnect clients --- client/src/lib.rs | 12 +++++++----- server/src/client.rs | 4 +++- server/src/events/player.rs | 14 +++++++++++++- server/src/lib.rs | 1 + 4 files changed, 24 insertions(+), 7 deletions(-) diff --git a/client/src/lib.rs b/client/src/lib.rs index 91db147335..c6ba6c119d 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -42,7 +42,7 @@ use std::{ sync::Arc, time::{Duration, Instant}, }; -use tracing::{debug, error, warn}; +use tracing::{debug, error, trace, warn}; use uvth::{ThreadPool, ThreadPoolBuilder}; use vek::*; @@ -312,10 +312,11 @@ impl Client { /// Send disconnect message to the server pub fn request_logout(&mut self) { + debug!("Requesting logout from server"); if let Err(e) = self.singleton_stream.send(ClientMsg::Disconnect) { error!( ?e, - "couldn't send disconnect package to server, did server close already?" + "Couldn't send disconnect package to server, did server close already?" ); } } @@ -1191,11 +1192,12 @@ impl Client { impl Drop for Client { fn drop(&mut self) { + trace!("Dropping client"); if let Err(e) = self.singleton_stream.send(ClientMsg::Disconnect) { warn!( - "error during drop of client, couldn't send disconnect package, is the connection \ - already closed? : {}", - e + ?e, + "Error during drop of client, couldn't send disconnect package, is the connection \ + already closed?", ); } } diff --git a/server/src/client.rs b/server/src/client.rs index 412d36a7d1..ea3ecce838 100644 --- a/server/src/client.rs +++ b/server/src/client.rs @@ -1,12 +1,14 @@ use common::msg::{ClientState, RequestStateError, ServerMsg}; use hashbrown::HashSet; -use network::Stream; +use network::{Participant, Stream}; use specs::{Component, FlaggedStorage}; use specs_idvs::IdvStorage; +use std::sync::{Arc, Mutex}; use vek::*; pub struct Client { pub client_state: ClientState, + pub participant: Mutex>>, pub singleton_stream: Stream, pub last_ping: f64, pub login_msg_sent: bool, diff --git a/server/src/events/player.rs b/server/src/events/player.rs index aae303c47f..53286ab115 100644 --- a/server/src/events/player.rs +++ b/server/src/events/player.rs @@ -8,8 +8,9 @@ use common::{ msg::{ClientState, PlayerListUpdate, ServerMsg}, sync::{Uid, UidAllocator}, }; +use futures_executor::block_on; use specs::{saveload::MarkerAllocator, Builder, Entity as EcsEntity, WorldExt}; -use tracing::error; +use tracing::{debug, error, trace}; pub fn handle_exit_ingame(server: &mut Server, entity: EcsEntity) { let state = server.state_mut(); @@ -46,6 +47,17 @@ pub fn handle_exit_ingame(server: &mut Server, entity: EcsEntity) { } pub fn handle_client_disconnect(server: &mut Server, entity: EcsEntity) -> Event { + if let Some(client) = server.state().read_storage::().get(entity) { + trace!("closing participant of client"); + let participant = client.participant.lock().unwrap().take().unwrap(); + if let Err(e) = block_on(server.network.disconnect(participant)) { + debug!( + ?e, + "Error when disconnecting client, maybe the pipe already broke" + ); + }; + } + let state = server.state_mut(); // Tell other clients to remove from player list diff --git a/server/src/lib.rs b/server/src/lib.rs index 1817330397..d0943b2d12 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -601,6 +601,7 @@ impl Server { let mut client = Client { client_state: ClientState::Connected, + participant: std::sync::Mutex::new(Some(participant)), singleton_stream, last_ping: self.state.get_time(), login_msg_sent: false, From 041349be48cdd32b8d8c237a2b9279ea8317fe63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=A4rtens?= Date: Thu, 9 Jul 2020 09:58:21 +0200 Subject: [PATCH 21/85] Switch API to return Participant rather than Arc - API behavior switched! - the `Network` no longer holds a copy of participant, thus if the return of `connect` (before `Arc, now `Participant`) got dropped, the `Participant::Drop` is triggered! - you can close a Participant async via `Particiant::disconnect()`, no more need to know the network at this point - the `Network::Drop` will check and drop not yet disconnected Participants. - you can compare Participants via PartialEq, if they are true they point to the same endpoint (it checks remote_pid) - Note: multiple Participants are only supported in theory, wont work yet Additionally: - fix some `debug!` - veloren-client will now drop the participant gracefully on shutdown - rename `error` to `debug` when 2 times Bparticipant shutdown is called, as it is to be expected in a async runtime --- client/src/lib.rs | 7 +- network/examples/chat/Cargo.lock | 47 ++- network/examples/chat/src/main.rs | 19 +- network/examples/fileshare/Cargo.lock | 47 ++- network/examples/fileshare/src/commands.rs | 6 +- network/examples/fileshare/src/server.rs | 5 +- network/examples/network-speed/Cargo.lock | 47 ++- network/examples/network-speed/src/main.rs | 2 +- network/src/api.rs | 330 +++++++++++---------- network/src/lib.rs | 3 +- network/src/participant.rs | 11 +- network/src/scheduler.rs | 1 + network/tests/closing.rs | 58 +++- network/tests/helper.rs | 14 +- network/tests/integration.rs | 12 +- server/src/client.rs | 4 +- server/src/events/player.rs | 2 +- 17 files changed, 384 insertions(+), 231 deletions(-) diff --git a/client/src/lib.rs b/client/src/lib.rs index c6ba6c119d..0644006433 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -75,7 +75,7 @@ pub struct Client { pub active_character_id: Option, _network: Network, - _participant: Arc, + participant: Option, singleton_stream: Stream, last_server_ping: f64, @@ -200,7 +200,7 @@ impl Client { active_character_id: None, _network: network, - _participant: participant, + participant: Some(participant), singleton_stream: stream, last_server_ping: 0.0, @@ -1200,5 +1200,8 @@ impl Drop for Client { already closed?", ); } + if let Err(e) = block_on(self.participant.take().unwrap().disconnect()) { + warn!(?e, "error when disconnecting, couldn't send all data"); + } } } diff --git a/network/examples/chat/Cargo.lock b/network/examples/chat/Cargo.lock index 9c26fd8f98..c5efb50889 100644 --- a/network/examples/chat/Cargo.lock +++ b/network/examples/chat/Cargo.lock @@ -65,7 +65,7 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5753e2a71534719bf3f4e57006c3a4f0d2c672a4b676eec84161f763eca87dbf" dependencies = [ - "byteorder", + "byteorder 1.3.4", "serde", ] @@ -75,6 +75,12 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +[[package]] +name = "byteorder" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fc10e8cc6b2580fda3f36eb6dc5316657f812a3df879a44a66fc9f0fdbc4855" + [[package]] name = "byteorder" version = "1.3.4" @@ -345,6 +351,16 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "lz4-compress" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f966533a922a9bba9e95e594c1fdb3b9bf5fdcdb11e37e51ad84cd76e468b91" +dependencies = [ + "byteorder 0.5.3", + "quick-error", +] + [[package]] name = "matchers" version = "0.0.1" @@ -538,15 +554,15 @@ dependencies = [ [[package]] name = "prometheus" -version = "0.7.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5567486d5778e2c6455b1b90ff1c558f29e751fc018130fa182e15828e728af1" +checksum = "dd0ced56dee39a6e960c15c74dc48849d614586db2eaada6497477af7c7811cd" dependencies = [ "cfg-if", "fnv", "lazy_static", - "quick-error", "spin", + "thiserror", ] [[package]] @@ -629,7 +645,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae1ded71d66a4a97f5e961fd0cb25a5f366a42a41570d16a763a69c092c26ae4" dependencies = [ - "byteorder", + "byteorder 1.3.4", "regex-syntax", ] @@ -712,6 +728,26 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "thiserror" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dfdd070ccd8ccb78f4ad66bf1982dc37f620ef696c6b5028fe2ed83dd3d0d08" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd80fc12f73063ac132ac92aceea36734f04a1d93c1240c6944e23a3b8841793" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "thread_local" version = "1.0.1" @@ -798,6 +834,7 @@ dependencies = [ "crossbeam-channel", "futures", "lazy_static", + "lz4-compress", "prometheus", "rand", "serde", diff --git a/network/examples/chat/src/main.rs b/network/examples/chat/src/main.rs index a3a4fabcab..a497567525 100644 --- a/network/examples/chat/src/main.rs +++ b/network/examples/chat/src/main.rs @@ -4,10 +4,11 @@ //! (cd network/examples/chat && RUST_BACKTRACE=1 cargo run --release -- --trace=info --port 15006 --mode=client) //! ``` use async_std::io; +use async_std::sync::RwLock; use clap::{App, Arg}; use futures::executor::{block_on, ThreadPool}; use network::{Address, Network, Participant, Pid, PROMISES_CONSISTENCY, PROMISES_ORDERED}; -use std::{sync::Arc, thread, time::Duration}; +use std::{sync::Arc, thread, time::Duration, collections::HashMap}; use tracing::*; use tracing_subscriber::EnvFilter; @@ -103,17 +104,19 @@ fn server(address: Address) { let server = Arc::new(server); std::thread::spawn(f); let pool = ThreadPool::new().unwrap(); + let participants = Arc::new(RwLock::new(HashMap::new())); block_on(async { server.listen(address).await.unwrap(); loop { - let p1 = server.connected().await.unwrap(); + let p1 = Arc::new(server.connected().await.unwrap()); let server1 = server.clone(); - pool.spawn_ok(client_connection(server1, p1)); + participants.write().await.insert(p1.remote_pid(), p1.clone()); + pool.spawn_ok(client_connection(server1, p1, participants.clone())); } }); } -async fn client_connection(network: Arc, participant: Arc) { +async fn client_connection(_network: Arc, participant: Arc, participants: Arc>>>) { let mut s1 = participant.opened().await.unwrap(); let username = s1.recv::().await.unwrap(); println!("[{}] connected", username); @@ -124,14 +127,12 @@ async fn client_connection(network: Arc, participant: Arc) }, Ok(msg) => { println!("[{}]: {}", username, msg); - let mut parts = network.participants().await; - for (_, p) in parts.drain() { + for (_, p) in participants.read().await.iter() { match p .open(32, PROMISES_ORDERED | PROMISES_CONSISTENCY) .await { Err(_) => { - //probably disconnected, remove it - network.disconnect(p).await.unwrap(); + info!("error talking to client, //TODO drop it") }, Ok(mut s) => s.send((username.clone(), msg.clone())).unwrap(), }; @@ -180,7 +181,7 @@ fn client(address: Address) { // receiving i open and close a stream per message. this can be done easier but // this allows me to be quite lazy on the server side and just get a list of // all participants and send to them... -async fn read_messages(participant: Arc) { +async fn read_messages(participant: Participant) { while let Ok(mut s) = participant.opened().await { let (username, message) = s.recv::<(String, String)>().await.unwrap(); println!("[{}]: {}", username, message); diff --git a/network/examples/fileshare/Cargo.lock b/network/examples/fileshare/Cargo.lock index 0144ec3ac0..24eaae51a1 100644 --- a/network/examples/fileshare/Cargo.lock +++ b/network/examples/fileshare/Cargo.lock @@ -83,7 +83,7 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5753e2a71534719bf3f4e57006c3a4f0d2c672a4b676eec84161f763eca87dbf" dependencies = [ - "byteorder", + "byteorder 1.3.4", "serde", ] @@ -104,6 +104,12 @@ dependencies = [ "constant_time_eq", ] +[[package]] +name = "byteorder" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fc10e8cc6b2580fda3f36eb6dc5316657f812a3df879a44a66fc9f0fdbc4855" + [[package]] name = "byteorder" version = "1.3.4" @@ -418,6 +424,16 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "lz4-compress" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f966533a922a9bba9e95e594c1fdb3b9bf5fdcdb11e37e51ad84cd76e468b91" +dependencies = [ + "byteorder 0.5.3", + "quick-error", +] + [[package]] name = "matchers" version = "0.0.1" @@ -597,15 +613,15 @@ dependencies = [ [[package]] name = "prometheus" -version = "0.7.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5567486d5778e2c6455b1b90ff1c558f29e751fc018130fa182e15828e728af1" +checksum = "dd0ced56dee39a6e960c15c74dc48849d614586db2eaada6497477af7c7811cd" dependencies = [ "cfg-if", "fnv", "lazy_static", - "quick-error", "spin", + "thiserror", ] [[package]] @@ -699,7 +715,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae1ded71d66a4a97f5e961fd0cb25a5f366a42a41570d16a763a69c092c26ae4" dependencies = [ - "byteorder", + "byteorder 1.3.4", "regex-syntax", ] @@ -803,6 +819,26 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "thiserror" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dfdd070ccd8ccb78f4ad66bf1982dc37f620ef696c6b5028fe2ed83dd3d0d08" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd80fc12f73063ac132ac92aceea36734f04a1d93c1240c6944e23a3b8841793" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "thread_local" version = "1.0.1" @@ -889,6 +925,7 @@ dependencies = [ "crossbeam-channel", "futures", "lazy_static", + "lz4-compress", "prometheus", "rand", "serde", diff --git a/network/examples/fileshare/src/commands.rs b/network/examples/fileshare/src/commands.rs index 3749f29c53..a603890a22 100644 --- a/network/examples/fileshare/src/commands.rs +++ b/network/examples/fileshare/src/commands.rs @@ -6,7 +6,7 @@ use network::{Address, Participant, Stream}; use rand::Rng; use serde::{Deserialize, Serialize}; -use std::{collections::HashMap, sync::Arc}; +use std::collections::HashMap; #[derive(Debug)] pub enum LocalCommand { @@ -34,7 +34,7 @@ pub struct FileInfo { pub struct RemoteInfo { infos: HashMap, - _participant: Arc, + _participant: Participant, pub cmd_out: Stream, pub file_out: Stream, } @@ -68,7 +68,7 @@ impl FileInfo { } impl RemoteInfo { - pub fn new(cmd_out: Stream, file_out: Stream, participant: Arc) -> Self { + pub fn new(cmd_out: Stream, file_out: Stream, participant: Participant) -> Self { Self { infos: HashMap::new(), _participant: participant, diff --git a/network/examples/fileshare/src/server.rs b/network/examples/fileshare/src/server.rs index c00cecd61d..2dc3835437 100644 --- a/network/examples/fileshare/src/server.rs +++ b/network/examples/fileshare/src/server.rs @@ -64,9 +64,6 @@ impl Server { }, LocalCommand::Disconnect => { self.remotes.write().await.clear(); - for (_, p) in self.network.participants().await.drain() { - self.network.disconnect(p).await.unwrap(); - } println!("Disconnecting all connections"); return; }, @@ -126,7 +123,7 @@ impl Server { trace!("Stop connect_manager"); } - async fn loop_participant(&self, p: Arc) { + async fn loop_participant(&self, p: Participant) { if let (Ok(cmd_out), Ok(file_out), Ok(cmd_in), Ok(file_in)) = ( p.open(15, PROMISES_CONSISTENCY | PROMISES_ORDERED).await, p.open(40, PROMISES_CONSISTENCY).await, diff --git a/network/examples/network-speed/Cargo.lock b/network/examples/network-speed/Cargo.lock index 7cd1b8e982..3a1ba21cb5 100644 --- a/network/examples/network-speed/Cargo.lock +++ b/network/examples/network-speed/Cargo.lock @@ -71,7 +71,7 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5753e2a71534719bf3f4e57006c3a4f0d2c672a4b676eec84161f763eca87dbf" dependencies = [ - "byteorder", + "byteorder 1.3.4", "serde", ] @@ -81,6 +81,12 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +[[package]] +name = "byteorder" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fc10e8cc6b2580fda3f36eb6dc5316657f812a3df879a44a66fc9f0fdbc4855" + [[package]] name = "byteorder" version = "1.3.4" @@ -372,6 +378,16 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "lz4-compress" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f966533a922a9bba9e95e594c1fdb3b9bf5fdcdb11e37e51ad84cd76e468b91" +dependencies = [ + "byteorder 0.5.3", + "quick-error", +] + [[package]] name = "matchers" version = "0.0.1" @@ -578,16 +594,16 @@ dependencies = [ [[package]] name = "prometheus" -version = "0.7.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5567486d5778e2c6455b1b90ff1c558f29e751fc018130fa182e15828e728af1" +checksum = "dd0ced56dee39a6e960c15c74dc48849d614586db2eaada6497477af7c7811cd" dependencies = [ "cfg-if", "fnv", "lazy_static", "protobuf", - "quick-error", "spin", + "thiserror", ] [[package]] @@ -670,7 +686,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae1ded71d66a4a97f5e961fd0cb25a5f366a42a41570d16a763a69c092c26ae4" dependencies = [ - "byteorder", + "byteorder 1.3.4", "regex-syntax", ] @@ -753,6 +769,26 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "thiserror" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dfdd070ccd8ccb78f4ad66bf1982dc37f620ef696c6b5028fe2ed83dd3d0d08" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd80fc12f73063ac132ac92aceea36734f04a1d93c1240c6944e23a3b8841793" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "thread_local" version = "1.0.1" @@ -880,6 +916,7 @@ dependencies = [ "crossbeam-channel", "futures", "lazy_static", + "lz4-compress", "prometheus", "rand", "serde", diff --git a/network/examples/network-speed/src/main.rs b/network/examples/network-speed/src/main.rs index f7b8e3a033..f047983394 100644 --- a/network/examples/network-speed/src/main.rs +++ b/network/examples/network-speed/src/main.rs @@ -181,7 +181,7 @@ fn client(address: Address) { drop(s1); std::thread::sleep(std::time::Duration::from_millis(5000)); info!("Closing participant"); - block_on(client.disconnect(p1)).unwrap(); + block_on(p1.disconnect()).unwrap(); std::thread::sleep(std::time::Duration::from_millis(25000)); info!("DROPPING! client"); drop(client); diff --git a/network/src/api.rs b/network/src/api.rs index 8a954c930b..8ef1d0918c 100644 --- a/network/src/api.rs +++ b/network/src/api.rs @@ -26,6 +26,9 @@ use std::{ use tracing::*; use tracing_futures::Instrument; +type ParticipantCloseChannel = + mpsc::UnboundedSender<(Pid, oneshot::Sender>)>; + /// Represents a Tcp or Udp or Mpsc address #[derive(Clone, Debug, Hash, PartialEq, Eq)] pub enum Address { @@ -47,8 +50,9 @@ pub struct Participant { a2b_steam_open_s: RwLock)>>, b2a_stream_opened_r: RwLock>, closed: AtomicBool, - a2s_disconnect_s: - Option>)>>, + //We need a std::Mutex here, the async Mutex requeres a block in `Drop` which can `panic!` + //It's only okay because `disconnect` is the only `fn` accessing it and it consumes self! + a2s_disconnect_s: Arc>>, } /// `Streams` represents a channel to send `n` messages with a certain priority @@ -83,13 +87,14 @@ pub enum NetworkError { NetworkClosed, ListenFailed(std::io::Error), ConnectFailed(std::io::Error), - GracefulDisconnectFailed(std::io::Error), } /// Error type thrown by [`Participants`](Participant) methods -#[derive(Debug, PartialEq)] +#[derive(Debug)] pub enum ParticipantError { + ///Participant was closed too early to complete the action fully ParticipantClosed, + GracefulDisconnectFailed(std::io::Error), } /// Error type thrown by [`Streams`](Stream) methods @@ -105,10 +110,9 @@ pub enum StreamError { /// Application. You can pass it around multiple threads in an /// [`Arc`](std::sync::Arc) as all commands have internal mutability. /// -/// The `Network` has methods to [`connect`] and [`disconnect`] to other -/// [`Participants`] via their [`Address`]. All [`Participants`] will be stored -/// in the Network until explicitly disconnected, which is the only way to close -/// the sockets. +/// The `Network` has methods to [`connect`] to other [`Participants`] actively +/// via their [`Address`], or [`listen`] passively for [`connected`] +/// [`Participants`]. /// /// # Examples /// ```rust @@ -133,10 +137,12 @@ pub enum StreamError { /// /// [`Participants`]: crate::api::Participant /// [`connect`]: Network::connect -/// [`disconnect`]: Network::disconnect +/// [`listen`]: Network::listen +/// [`connected`]: Network::connected pub struct Network { local_pid: Pid, - participants: RwLock>>, + participant_disconnect_sender: + RwLock>>>>, listen_sender: RwLock>)>>, connect_sender: @@ -204,7 +210,7 @@ impl Network { ( Self { local_pid: participant_id, - participants: RwLock::new(HashMap::new()), + participant_disconnect_sender: RwLock::new(HashMap::new()), listen_sender: RwLock::new(listen_sender), connect_sender: RwLock::new(connect_sender), connected_receiver: RwLock::new(connected_receiver), @@ -293,7 +299,7 @@ impl Network { /// let p2 = network /// .connect(Address::Udp("127.0.0.1:2011".parse().unwrap())) /// .await?; - /// assert!(std::sync::Arc::ptr_eq(&p1, &p2)); + /// assert_eq!(&p1, &p2); /// # Ok(()) /// }) /// # } @@ -307,7 +313,7 @@ impl Network { /// /// [`Streams`]: crate::api::Stream /// [`Addresses`]: crate::api::Address - pub async fn connect(&self, address: Address) -> Result, NetworkError> { + pub async fn connect(&self, address: Address) -> Result { let (pid_sender, pid_receiver) = oneshot::channel::>(); debug!(?address, "Connect to address"); self.connect_sender @@ -324,11 +330,10 @@ impl Network { ?pid, "Received Participant id from remote and return to user" ); - let participant = Arc::new(participant); - self.participants + self.participant_disconnect_sender .write() .await - .insert(participant.remote_pid, participant.clone()); + .insert(pid, participant.a2s_disconnect_s.clone()); Ok(participant) } @@ -365,129 +370,14 @@ impl Network { /// /// [`Streams`]: crate::api::Stream /// [`listen`]: crate::api::Network::listen - pub async fn connected(&self) -> Result, NetworkError> { + pub async fn connected(&self) -> Result { let participant = self.connected_receiver.write().await.next().await?; - let participant = Arc::new(participant); - self.participants + self.participant_disconnect_sender .write() .await - .insert(participant.remote_pid, participant.clone()); + .insert(participant.remote_pid, participant.a2s_disconnect_s.clone()); Ok(participant) } - - /// disconnecting a [`Participant`] where you move the last existing - /// [`Arc`]. As the [`Network`] also holds [`Arc`] to the - /// [`Participant`], you need to provide the last [`Arc`] and - /// are not allowed to keep others. If you do so the [`Participant`] - /// can't be disconnected properly. If you no longer have the respective - /// [`Participant`], try using the [`participants`] method to get it. - /// - /// This function will wait for all [`Streams`] to properly close, including - /// all messages to be send before closing. If an error occurs with one - /// of the messages. - /// Except if the remote side already dropped the [`Participant`] - /// simultaneously, then messages won't be sended - /// - /// There is NO `disconnected` function in `Network`, if a [`Participant`] - /// is no longer reachable (e.g. as the network cable was unplugged) the - /// [`Participant`] will fail all action, but needs to be manually - /// disconected, using this function. - /// - /// # Examples - /// ```rust - /// use futures::executor::block_on; - /// use veloren_network::{Address, Network, Pid}; - /// - /// # fn main() -> std::result::Result<(), Box> { - /// // Create a Network, listen on port `2030` TCP and opens returns their Pid and close connection. - /// let (network, f) = Network::new(Pid::new(), None); - /// std::thread::spawn(f); - /// # let (remote, fr) = Network::new(Pid::new(), None); - /// # std::thread::spawn(fr); - /// block_on(async { - /// network - /// .listen(Address::Tcp("0.0.0.0:2030".parse().unwrap())) - /// .await?; - /// # remote.connect(Address::Tcp("0.0.0.0:2030".parse().unwrap())).await?; - /// while let Ok(participant) = network.connected().await { - /// println!("Participant connected: {}", participant.remote_pid()); - /// network.disconnect(participant).await?; - /// # //skip test here as it would be a endless loop - /// # break; - /// } - /// # Ok(()) - /// }) - /// # } - /// ``` - /// - /// [`Arc`]: crate::api::Participant - /// [`Streams`]: crate::api::Stream - /// [`participants`]: Network::participants - /// [`Arc`]: std::sync::Arc - pub async fn disconnect(&self, participant: Arc) -> Result<(), NetworkError> { - // Remove, Close and try_unwrap error when unwrap fails! - let pid = participant.remote_pid; - debug!(?pid, "Removing participant from network"); - self.participants.write().await.remove(&pid)?; - participant.closed.store(true, Ordering::Relaxed); - - match Arc::try_unwrap(participant) { - Err(_) => { - warn!( - "You are disconnecting and still keeping a reference to this participant, \ - this is a bad idea. Participant will only be dropped when you drop your last \ - reference" - ); - Ok(()) - }, - Ok(mut participant) => { - trace!("Waiting now for participant to close"); - let (finished_sender, finished_receiver) = oneshot::channel(); - // we are deleting here asyncly before DROP is called. Because this is done - // nativly async, while drop needs an BLOCK! Drop will recognis - // that it has been delete here and don't try another double delete. - participant - .a2s_disconnect_s - .take() - .unwrap() - .send((pid, finished_sender)) - .await - .expect("Something is wrong in internal scheduler coding"); - match finished_receiver.await { - Ok(Ok(())) => { - trace!(?pid, "Participant is now closed"); - Ok(()) - }, - Ok(Err(e)) => { - trace!( - ?e, - "Error occured during shutdown of participant and is propagated to \ - User" - ); - Err(NetworkError::GracefulDisconnectFailed(e)) - }, - Err(e) => { - error!( - ?pid, - ?e, - "Failed to get a message back from the scheduler, closing the network" - ); - Err(NetworkError::NetworkClosed) - }, - } - }, - } - } - - /// returns a copy of all current connected [`Participants`], - /// including ones, which can't send data anymore as the underlying sockets - /// are closed already but haven't been [`disconnected`] yet. - /// - /// [`Participants`]: crate::api::Participant - /// [`disconnected`]: Network::disconnect - pub async fn participants(&self) -> HashMap> { - self.participants.read().await.clone() - } } impl Participant { @@ -504,7 +394,7 @@ impl Participant { a2b_steam_open_s: RwLock::new(a2b_steam_open_s), b2a_stream_opened_r: RwLock::new(b2a_stream_opened_r), closed: AtomicBool::new(false), - a2s_disconnect_s: Some(a2s_disconnect_s), + a2s_disconnect_s: Arc::new(std::sync::Mutex::new(Some(a2s_disconnect_s))), } } @@ -635,6 +525,100 @@ impl Participant { } } + /// disconnecting a `Participant` in a async way. + /// Use this rather than `Participant::Drop` if you want to close multiple + /// `Participants`. + /// + /// This function will wait for all [`Streams`] to properly close, including + /// all messages to be send before closing. If an error occurs with one + /// of the messages. + /// Except if the remote side already dropped the `Participant` + /// simultaneously, then messages won't be send + /// + /// There is NO `disconnected` function in `Participant`, if a `Participant` + /// is no longer reachable (e.g. as the network cable was unplugged) the + /// `Participant` will fail all action, but needs to be manually + /// disconected, using this function. + /// + /// # Examples + /// ```rust + /// use futures::executor::block_on; + /// use veloren_network::{Address, Network, Pid}; + /// + /// # fn main() -> std::result::Result<(), Box> { + /// // Create a Network, listen on port `2030` TCP and opens returns their Pid and close connection. + /// let (network, f) = Network::new(Pid::new(), None); + /// std::thread::spawn(f); + /// # let (remote, fr) = Network::new(Pid::new(), None); + /// # std::thread::spawn(fr); + /// block_on(async { + /// network + /// .listen(Address::Tcp("0.0.0.0:2030".parse().unwrap())) + /// .await?; + /// # remote.connect(Address::Tcp("0.0.0.0:2030".parse().unwrap())).await?; + /// while let Ok(participant) = network.connected().await { + /// println!("Participant connected: {}", participant.remote_pid()); + /// participant.disconnect().await?; + /// # //skip test here as it would be a endless loop + /// # break; + /// } + /// # Ok(()) + /// }) + /// # } + /// ``` + /// + /// [`Streams`]: crate::api::Stream + pub async fn disconnect(self) -> Result<(), ParticipantError> { + // Remove, Close and try_unwrap error when unwrap fails! + let pid = self.remote_pid; + debug!(?pid, "Closing participant from network"); + self.closed.store(true, Ordering::Relaxed); + //Streams will be closed by BParticipant + + match self.a2s_disconnect_s.lock().unwrap().take() { + Some(mut a2s_disconnect_s) => { + let (finished_sender, finished_receiver) = oneshot::channel(); + // Participant is connecting to Scheduler here, not as usual + // Participant<->BParticipant + a2s_disconnect_s + .send((pid, finished_sender)) + .await + .expect("Something is wrong in internal scheduler coding"); + match finished_receiver.await { + Ok(Ok(())) => { + trace!(?pid, "Participant is now closed"); + Ok(()) + }, + Ok(Err(e)) => { + trace!( + ?e, + "Error occured during shutdown of participant and is propagated to \ + User" + ); + Err(ParticipantError::GracefulDisconnectFailed(e)) + }, + Err(e) => { + //this is a bug. but as i am Participant i can't destroy the network + error!( + ?pid, + ?e, + "Failed to get a message back from the scheduler, seems like the \ + network is already closed" + ); + Err(ParticipantError::ParticipantClosed) + }, + } + }, + None => { + warn!( + "seems like you are trying to disconnecting a participant after the network \ + was already dropped. It was already dropped with the network!" + ); + Err(ParticipantError::ParticipantClosed) + }, + } + } + /// Returns the remote [`Pid`] pub fn remote_pid(&self) -> Pid { self.remote_pid } } @@ -837,38 +821,68 @@ impl Stream { } } +/// +impl core::cmp::PartialEq for Participant { + fn eq(&self, other: &Self) -> bool { + //don't check local_pid, 2 Participant from different network should match if + // they are the "same" + self.remote_pid == other.remote_pid + } +} + impl Drop for Network { fn drop(&mut self) { let pid = self.local_pid; debug!(?pid, "Shutting down Network"); - debug!( + trace!( ?pid, "Shutting down Participants of Network, while we still have metrics" ); + let mut finished_receiver_list = vec![]; task::block_on(async { // we need to carefully shut down here! as otherwise we might call // Participant::Drop with a2s_disconnect_s here which would open - // another task::block, which would panic! also i can't `.write` on - // `self.participants` as the `disconnect` fn needs it. - let mut participant_clone = self.participants().await; - for (_, p) in participant_clone.drain() { - if let Err(e) = self.disconnect(p).await { - error!( - ?e, - "Error while dropping network, the error occured when dropping a \ - participant but can't be notified to the user any more" - ); + // another task::block, which would panic! + for (remote_pid, a2s_disconnect_s) in + self.participant_disconnect_sender.write().await.drain() + { + match a2s_disconnect_s.lock().unwrap().take() { + Some(mut a2s_disconnect_s) => { + trace!(?remote_pid, "Participants will be closed"); + let (finished_sender, finished_receiver) = oneshot::channel(); + finished_receiver_list.push((remote_pid, finished_receiver)); + a2s_disconnect_s + .send((remote_pid, finished_sender)) + .await + .expect( + "Scheduler is closed, but nobody other should be able to close it", + ); + }, + None => trace!(?remote_pid, "Participant already disconnected gracefully"), + } + } + //wait after close is requested for all + for (remote_pid, finished_receiver) in finished_receiver_list.drain(..) { + match finished_receiver.await { + Ok(Ok(())) => trace!(?remote_pid, "disconnect successful"), + Ok(Err(e)) => info!(?remote_pid, ?e, "unclean disconnect"), + Err(e) => warn!( + ?remote_pid, + ?e, + "Failed to get a message back from the scheduler, seems like the network \ + is already closed" + ), } } - self.participants.write().await.clear(); }); - debug!(?pid, "Shutting down Scheduler"); + trace!(?pid, "Participants have shut down!"); + trace!(?pid, "Shutting down Scheduler"); self.shutdown_sender .take() .unwrap() .send(()) .expect("Scheduler is closed, but nobody other should be able to close it"); - debug!(?pid, "Participants have shut down!"); + debug!(?pid, "Network has shut down"); } } @@ -878,16 +892,14 @@ impl Drop for Participant { // participant from network let pid = self.remote_pid; debug!(?pid, "Shutting down Participant"); - match self.a2s_disconnect_s.take() { - None => debug!( + + match self.a2s_disconnect_s.lock().unwrap().take() { + None => trace!( ?pid, "Participant has been shutdown cleanly, no further waiting is requiered!" ), Some(mut a2s_disconnect_s) => { - debug!( - ?pid, - "Unclean shutdown detected, active waiting for client to be disconnected" - ); + debug!(?pid, "Disconnect from Scheduler"); task::block_on(async { let (finished_sender, finished_receiver) = oneshot::channel(); a2s_disconnect_s @@ -912,7 +924,7 @@ impl Drop for Participant { }); }, } - debug!(?pid, "Network dropped"); + debug!(?pid, "Participant dropped"); } } @@ -932,7 +944,7 @@ impl Drop for Stream { } else { let sid = self.sid; let pid = self.pid; - debug!(?pid, ?sid, "Drop not needed"); + trace!(?pid, ?sid, "Stream Drop not needed"); } } } @@ -1012,6 +1024,9 @@ impl core::fmt::Display for ParticipantError { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { ParticipantError::ParticipantClosed => write!(f, "Participant closed"), + ParticipantError::GracefulDisconnectFailed(_) => { + write!(f, "Graceful disconnect failed") + }, } } } @@ -1022,7 +1037,6 @@ impl core::fmt::Display for NetworkError { NetworkError::NetworkClosed => write!(f, "Network closed"), NetworkError::ListenFailed(_) => write!(f, "Listening failed"), NetworkError::ConnectFailed(_) => write!(f, "Connecting failed"), - NetworkError::GracefulDisconnectFailed(_) => write!(f, "Graceful disconnect failed"), } } } diff --git a/network/src/lib.rs b/network/src/lib.rs index eea927189c..2cbb2bc494 100644 --- a/network/src/lib.rs +++ b/network/src/lib.rs @@ -16,7 +16,7 @@ //! //! To connect to another application, you must know it's [`Address`]. One side //! will call [`connect`], the other [`connected`]. If successfull both -//! applications will now get a [`Arc`]. +//! applications will now get a [`Participant`]. //! //! This [`Participant`] represents the connection between those 2 applications. //! over the respective [`Address`] and with it the choosen network protocol. @@ -86,7 +86,6 @@ //! [`Networks`]: crate::api::Network //! [`connect`]: crate::api::Network::connect //! [`connected`]: crate::api::Network::connected -//! [`Arc`]: crate::api::Participant //! [`Participant`]: crate::api::Participant //! [`Participants`]: crate::api::Participant //! [`open`]: crate::api::Participant::open diff --git a/network/src/participant.rs b/network/src/participant.rs index 7dac6102bf..fd6c32d798 100644 --- a/network/src/participant.rs +++ b/network/src/participant.rs @@ -252,8 +252,8 @@ impl BParticipant { longer work in the first place" ); }; - //TODO - warn!( + //TODO FIXME tags: takeover channel multiple + info!( "FIXME: the frame is actually drop. which is fine for now as the participant \ will be closed, but not if we do channel-takeover" ); @@ -621,10 +621,9 @@ impl BParticipant { let mut info = match self.shutdown_info.lock().await.take() { Some(info) => info, None => { - error!( - "Close of participant seemed to be called twice, that's bad, ignoring the 2nd \ - close" - ); + //This can happen if >=2 different async fn found out the protocol got dropped + // but they haven't shut down so far + debug!("Close of participant seemed to be called twice, ignoring the 2nd close"); return; }, }; diff --git a/network/src/scheduler.rs b/network/src/scheduler.rs index 5264a89244..6fd45c7950 100644 --- a/network/src/scheduler.rs +++ b/network/src/scheduler.rs @@ -328,6 +328,7 @@ impl Scheduler { ); }; } + debug!("Scheduler shut down gracefully"); //removing the possibility to create new participants, needed to close down // some mgr: self.participant_channels.lock().await.take(); diff --git a/network/tests/closing.rs b/network/tests/closing.rs index 25ac05311c..2bd9a92acd 100644 --- a/network/tests/closing.rs +++ b/network/tests/closing.rs @@ -19,10 +19,10 @@ fn close_network() { #[test] fn close_participant() { let (_, _) = helper::setup(false, 0); - let (n_a, p1_a, mut s1_a, n_b, p1_b, mut s1_b) = block_on(network_participant_stream(tcp())); + let (_n_a, p1_a, mut s1_a, _n_b, p1_b, mut s1_b) = block_on(network_participant_stream(tcp())); - block_on(n_a.disconnect(p1_a)).unwrap(); - block_on(n_b.disconnect(p1_b)).unwrap(); + block_on(p1_a.disconnect()).unwrap(); + block_on(p1_b.disconnect()).unwrap(); assert_eq!(s1_a.send("Hello World"), Err(StreamError::StreamClosed)); assert_eq!( @@ -66,7 +66,7 @@ fn close_streams_in_block_on() { #[test] fn stream_simple_3msg_then_close() { let (_, _) = helper::setup(false, 0); - let (_n_a, _, mut s1_a, _n_b, _, mut s1_b) = block_on(network_participant_stream(tcp())); + let (_n_a, _p_a, mut s1_a, _n_b, _p_b, mut s1_b) = block_on(network_participant_stream(tcp())); s1_a.send(1u8).unwrap(); s1_a.send(42).unwrap(); @@ -83,7 +83,7 @@ fn stream_simple_3msg_then_close() { fn stream_send_first_then_receive() { // recv should still be possible even if stream got closed if they are in queue let (_, _) = helper::setup(false, 0); - let (_n_a, _, mut s1_a, _n_b, _, mut s1_b) = block_on(network_participant_stream(tcp())); + let (_n_a, _p_a, mut s1_a, _n_b, _p_b, mut s1_b) = block_on(network_participant_stream(tcp())); s1_a.send(1u8).unwrap(); s1_a.send(42).unwrap(); @@ -99,7 +99,7 @@ fn stream_send_first_then_receive() { #[test] fn stream_send_1_then_close_stream() { let (_, _) = helper::setup(false, 0); - let (_n_a, _, mut s1_a, _n_b, _, mut s1_b) = block_on(network_participant_stream(tcp())); + let (_n_a, _p_a, mut s1_a, _n_b, _p_b, mut s1_b) = block_on(network_participant_stream(tcp())); s1_a.send("this message must be received, even if stream is closed already!") .unwrap(); drop(s1_a); @@ -112,7 +112,7 @@ fn stream_send_1_then_close_stream() { #[test] fn stream_send_100000_then_close_stream() { let (_, _) = helper::setup(false, 0); - let (_n_a, _, mut s1_a, _n_b, _, mut s1_b) = block_on(network_participant_stream(tcp())); + let (_n_a, _p_a, mut s1_a, _n_b, _p_b, mut s1_b) = block_on(network_participant_stream(tcp())); for _ in 0..100000 { s1_a.send("woop_PARTY_HARD_woop").unwrap(); } @@ -130,7 +130,7 @@ fn stream_send_100000_then_close_stream() { #[test] fn stream_send_100000_then_close_stream_remote() { let (_, _) = helper::setup(false, 0); - let (_n_a, _, mut s1_a, _n_b, _, _s1_b) = block_on(network_participant_stream(tcp())); + let (_n_a, _p_a, mut s1_a, _n_b, _p_b, _s1_b) = block_on(network_participant_stream(tcp())); for _ in 0..100000 { s1_a.send("woop_PARTY_HARD_woop").unwrap(); } @@ -142,7 +142,7 @@ fn stream_send_100000_then_close_stream_remote() { #[test] fn stream_send_100000_then_close_stream_remote2() { let (_, _) = helper::setup(false, 0); - let (_n_a, _, mut s1_a, _n_b, _, _s1_b) = block_on(network_participant_stream(tcp())); + let (_n_a, _p_a, mut s1_a, _n_b, _p_b, _s1_b) = block_on(network_participant_stream(tcp())); for _ in 0..100000 { s1_a.send("woop_PARTY_HARD_woop").unwrap(); } @@ -155,7 +155,7 @@ fn stream_send_100000_then_close_stream_remote2() { #[test] fn stream_send_100000_then_close_stream_remote3() { let (_, _) = helper::setup(false, 0); - let (_n_a, _, mut s1_a, _n_b, _, _s1_b) = block_on(network_participant_stream(tcp())); + let (_n_a, _p_a, mut s1_a, _n_b, _p_b, _s1_b) = block_on(network_participant_stream(tcp())); for _ in 0..100000 { s1_a.send("woop_PARTY_HARD_woop").unwrap(); } @@ -164,3 +164,41 @@ fn stream_send_100000_then_close_stream_remote3() { drop(s1_a); //no receiving } + +#[test] +fn close_part_then_network() { + let (_, _) = helper::setup(false, 0); + let (n_a, p_a, mut s1_a, _n_b, _p_b, _s1_b) = block_on(network_participant_stream(tcp())); + for _ in 0..1000 { + s1_a.send("woop_PARTY_HARD_woop").unwrap(); + } + drop(p_a); + std::thread::sleep(std::time::Duration::from_millis(1000)); + drop(n_a); + std::thread::sleep(std::time::Duration::from_millis(1000)); +} + +#[test] +fn close_network_then_part() { + let (_, _) = helper::setup(false, 0); + let (n_a, p_a, mut s1_a, _n_b, _p_b, _s1_b) = block_on(network_participant_stream(tcp())); + for _ in 0..1000 { + s1_a.send("woop_PARTY_HARD_woop").unwrap(); + } + drop(n_a); + std::thread::sleep(std::time::Duration::from_millis(1000)); + drop(p_a); + std::thread::sleep(std::time::Duration::from_millis(1000)); +} + +#[test] +fn close_network_then_disconnect_part() { + let (_, _) = helper::setup(false, 0); + let (n_a, p_a, mut s1_a, _n_b, _p_b, _s1_b) = block_on(network_participant_stream(tcp())); + for _ in 0..1000 { + s1_a.send("woop_PARTY_HARD_woop").unwrap(); + } + drop(n_a); + assert!(block_on(p_a.disconnect()).is_err()); + std::thread::sleep(std::time::Duration::from_millis(1000)); +} diff --git a/network/tests/helper.rs b/network/tests/helper.rs index f043074e8e..386f6d94dc 100644 --- a/network/tests/helper.rs +++ b/network/tests/helper.rs @@ -1,10 +1,7 @@ use lazy_static::*; use std::{ net::SocketAddr, - sync::{ - atomic::{AtomicU16, Ordering}, - Arc, - }, + sync::atomic::{AtomicU16, Ordering}, thread, time::Duration, }; @@ -51,14 +48,7 @@ pub fn setup(tracing: bool, mut sleep: u64) -> (u64, u64) { #[allow(dead_code)] pub async fn network_participant_stream( addr: Address, -) -> ( - Network, - Arc, - Stream, - Network, - Arc, - Stream, -) { +) -> (Network, Participant, Stream, Network, Participant, Stream) { let (n_a, f_a) = Network::new(Pid::fake(1), None); std::thread::spawn(f_a); let (n_b, f_b) = Network::new(Pid::fake(2), None); diff --git a/network/tests/integration.rs b/network/tests/integration.rs index 6fe0dea42a..e0cd43844f 100644 --- a/network/tests/integration.rs +++ b/network/tests/integration.rs @@ -17,7 +17,7 @@ fn network_20s() { #[test] fn stream_simple() { let (_, _) = helper::setup(false, 0); - let (_n_a, _, mut s1_a, _n_b, _, mut s1_b) = block_on(network_participant_stream(tcp())); + let (_n_a, _p_a, mut s1_a, _n_b, _p_b, mut s1_b) = block_on(network_participant_stream(tcp())); s1_a.send("Hello World").unwrap(); assert_eq!(block_on(s1_b.recv()), Ok("Hello World".to_string())); @@ -26,7 +26,7 @@ fn stream_simple() { #[test] fn stream_simple_3msg() { let (_, _) = helper::setup(false, 0); - let (_n_a, _, mut s1_a, _n_b, _, mut s1_b) = block_on(network_participant_stream(tcp())); + let (_n_a, _p_a, mut s1_a, _n_b, _p_b, mut s1_b) = block_on(network_participant_stream(tcp())); s1_a.send("Hello World").unwrap(); s1_a.send(1337).unwrap(); @@ -39,7 +39,7 @@ fn stream_simple_3msg() { #[test] fn stream_simple_udp() { let (_, _) = helper::setup(false, 0); - let (_n_a, _, mut s1_a, _n_b, _, mut s1_b) = block_on(network_participant_stream(udp())); + let (_n_a, _p_a, mut s1_a, _n_b, _p_b, mut s1_b) = block_on(network_participant_stream(udp())); s1_a.send("Hello World").unwrap(); assert_eq!(block_on(s1_b.recv()), Ok("Hello World".to_string())); @@ -48,7 +48,7 @@ fn stream_simple_udp() { #[test] fn stream_simple_udp_3msg() { let (_, _) = helper::setup(false, 0); - let (_n_a, _, mut s1_a, _n_b, _, mut s1_b) = block_on(network_participant_stream(udp())); + let (_n_a, _p_a, mut s1_a, _n_b, _p_b, mut s1_b) = block_on(network_participant_stream(udp())); s1_a.send("Hello World").unwrap(); s1_a.send(1337).unwrap(); @@ -79,7 +79,7 @@ fn tcp_and_udp_2_connections() -> std::result::Result<(), Box std::result::Result<(), Box> #[test] fn wrong_parse() { let (_, _) = helper::setup(false, 0); - let (_n_a, _, mut s1_a, _n_b, _, mut s1_b) = block_on(network_participant_stream(tcp())); + let (_n_a, _p_a, mut s1_a, _n_b, _p_b, mut s1_b) = block_on(network_participant_stream(tcp())); s1_a.send(1337).unwrap(); match block_on(s1_b.recv::()) { diff --git a/server/src/client.rs b/server/src/client.rs index ea3ecce838..345d674381 100644 --- a/server/src/client.rs +++ b/server/src/client.rs @@ -3,12 +3,12 @@ use hashbrown::HashSet; use network::{Participant, Stream}; use specs::{Component, FlaggedStorage}; use specs_idvs::IdvStorage; -use std::sync::{Arc, Mutex}; +use std::sync::Mutex; use vek::*; pub struct Client { pub client_state: ClientState, - pub participant: Mutex>>, + pub participant: Mutex>, pub singleton_stream: Stream, pub last_ping: f64, pub login_msg_sent: bool, diff --git a/server/src/events/player.rs b/server/src/events/player.rs index 53286ab115..c01afd405e 100644 --- a/server/src/events/player.rs +++ b/server/src/events/player.rs @@ -50,7 +50,7 @@ pub fn handle_client_disconnect(server: &mut Server, entity: EcsEntity) -> Event if let Some(client) = server.state().read_storage::().get(entity) { trace!("closing participant of client"); let participant = client.participant.lock().unwrap().take().unwrap(); - if let Err(e) = block_on(server.network.disconnect(participant)) { + if let Err(e) = block_on(participant.disconnect()) { debug!( ?e, "Error when disconnecting client, maybe the pipe already broke" From 9d32e3f8841c7d23a0fe37da379788b6cc840f63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=A4rtens?= Date: Thu, 9 Jul 2020 13:42:38 +0200 Subject: [PATCH 22/85] proper voxygen connect and code cleanups: - voxygen abort when the server has a invalid veloren_network handshake, e.g. by outdated version instead of try again - rename Network `Address` to `ProtocolAddr` as sugested by zest as it's a combination of Protocol and std::io::Addr - remove the manual byte arrays in `protocols.rs` with something more nice --- client/src/lib.rs | 6 +- network/examples/chat/src/main.rs | 10 +-- network/examples/fileshare/src/commands.rs | 4 +- network/examples/fileshare/src/main.rs | 8 +- network/examples/fileshare/src/server.rs | 4 +- network/examples/network-speed/src/main.rs | 10 +-- network/src/api.rs | 94 +++++++++++----------- network/src/lib.rs | 26 +++--- network/src/message.rs | 34 +++++++- network/src/prios.rs | 35 +------- network/src/protocols.rs | 51 ++++-------- network/src/scheduler.rs | 37 ++++----- network/tests/helper.rs | 12 +-- network/tests/integration.rs | 18 ++--- server/src/lib.rs | 4 +- voxygen/src/menu/main/client_init.rs | 42 +++++----- 16 files changed, 193 insertions(+), 202 deletions(-) diff --git a/client/src/lib.rs b/client/src/lib.rs index 0644006433..accc589818 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -35,7 +35,9 @@ use futures_timer::Delay; use futures_util::{select, FutureExt}; use hashbrown::HashMap; use image::DynamicImage; -use network::{Address, Network, Participant, Pid, Stream, PROMISES_CONSISTENCY, PROMISES_ORDERED}; +use network::{ + Network, Participant, Pid, ProtocolAddr, Stream, PROMISES_CONSISTENCY, PROMISES_ORDERED, +}; use std::{ collections::VecDeque, net::SocketAddr, @@ -117,7 +119,7 @@ impl Client { let (network, f) = Network::new(Pid::new(), None); thread_pool.execute(f); - let participant = block_on(network.connect(Address::Tcp(addr.into())))?; + let participant = block_on(network.connect(ProtocolAddr::Tcp(addr.into())))?; let mut stream = block_on(participant.open(10, PROMISES_ORDERED | PROMISES_CONSISTENCY))?; // Wait for initial sync diff --git a/network/examples/chat/src/main.rs b/network/examples/chat/src/main.rs index a497567525..4d9f08a8f3 100644 --- a/network/examples/chat/src/main.rs +++ b/network/examples/chat/src/main.rs @@ -7,7 +7,7 @@ use async_std::io; use async_std::sync::RwLock; use clap::{App, Arg}; use futures::executor::{block_on, ThreadPool}; -use network::{Address, Network, Participant, Pid, PROMISES_CONSISTENCY, PROMISES_ORDERED}; +use network::{ProtocolAddr, Network, Participant, Pid, PROMISES_CONSISTENCY, PROMISES_ORDERED}; use std::{sync::Arc, thread, time::Duration, collections::HashMap}; use tracing::*; use tracing_subscriber::EnvFilter; @@ -77,8 +77,8 @@ fn main() { let port: u16 = matches.value_of("port").unwrap().parse().unwrap(); let ip: &str = matches.value_of("ip").unwrap(); let address = match matches.value_of("protocol") { - Some("tcp") => Address::Tcp(format!("{}:{}", ip, port).parse().unwrap()), - Some("udp") => Address::Udp(format!("{}:{}", ip, port).parse().unwrap()), + Some("tcp") => ProtocolAddr::Tcp(format!("{}:{}", ip, port).parse().unwrap()), + Some("udp") => ProtocolAddr::Udp(format!("{}:{}", ip, port).parse().unwrap()), _ => panic!("invalid mode, run --help!"), }; @@ -99,7 +99,7 @@ fn main() { } } -fn server(address: Address) { +fn server(address: ProtocolAddr) { let (server, f) = Network::new(Pid::new(), None); let server = Arc::new(server); std::thread::spawn(f); @@ -143,7 +143,7 @@ async fn client_connection(_network: Arc, participant: Arc println!("[{}] disconnected", username); } -fn client(address: Address) { +fn client(address: ProtocolAddr) { let (client, f) = Network::new(Pid::new(), None); std::thread::spawn(f); let pool = ThreadPool::new().unwrap(); diff --git a/network/examples/fileshare/src/commands.rs b/network/examples/fileshare/src/commands.rs index a603890a22..9d92f00d95 100644 --- a/network/examples/fileshare/src/commands.rs +++ b/network/examples/fileshare/src/commands.rs @@ -2,7 +2,7 @@ use async_std::{ fs, path::{Path, PathBuf}, }; -use network::{Address, Participant, Stream}; +use network::{ProtocolAddr, Participant, Stream}; use rand::Rng; use serde::{Deserialize, Serialize}; @@ -12,7 +12,7 @@ use std::collections::HashMap; pub enum LocalCommand { Shutdown, Disconnect, - Connect(Address), + Connect(ProtocolAddr), List, Serve(FileInfo), Get(u32, Option), diff --git a/network/examples/fileshare/src/main.rs b/network/examples/fileshare/src/main.rs index 322afae3eb..a0fde1a622 100644 --- a/network/examples/fileshare/src/main.rs +++ b/network/examples/fileshare/src/main.rs @@ -10,7 +10,7 @@ use futures::{ executor::{block_on, ThreadPool}, sink::SinkExt, }; -use network::Address; +use network::ProtocolAddr; use std::{thread, time::Duration}; use tracing::*; use tracing_subscriber::EnvFilter; @@ -54,7 +54,7 @@ fn main() { .init(); let port: u16 = matches.value_of("port").unwrap().parse().unwrap(); - let address = Address::Tcp(format!("{}:{}", "127.0.0.1", port).parse().unwrap()); + let address = ProtocolAddr::Tcp(format!("{}:{}", "127.0.0.1", port).parse().unwrap()); let (server, cmd_sender) = Server::new(); let pool = ThreadPool::new().unwrap(); @@ -157,13 +157,13 @@ async fn client(mut cmd_sender: mpsc::UnboundedSender) { ("connect", Some(connect_matches)) => { let socketaddr = connect_matches.value_of("ip:port").unwrap().parse().unwrap(); cmd_sender - .send(LocalCommand::Connect(Address::Tcp(socketaddr))) + .send(LocalCommand::Connect(ProtocolAddr::Tcp(socketaddr))) .await .unwrap(); }, ("t", _) => { cmd_sender - .send(LocalCommand::Connect(Address::Tcp( + .send(LocalCommand::Connect(ProtocolAddr::Tcp( "127.0.0.1:1231".parse().unwrap(), ))) .await diff --git a/network/examples/fileshare/src/server.rs b/network/examples/fileshare/src/server.rs index 2dc3835437..50df21e65e 100644 --- a/network/examples/fileshare/src/server.rs +++ b/network/examples/fileshare/src/server.rs @@ -5,7 +5,7 @@ use async_std::{ sync::{Mutex, RwLock}, }; use futures::{channel::mpsc, future::FutureExt, stream::StreamExt}; -use network::{Address, Network, Participant, Pid, Stream, PROMISES_CONSISTENCY, PROMISES_ORDERED}; +use network::{ProtocolAddr, Network, Participant, Pid, Stream, PROMISES_CONSISTENCY, PROMISES_ORDERED}; use std::{collections::HashMap, sync::Arc}; use tracing::*; @@ -42,7 +42,7 @@ impl Server { ) } - pub async fn run(mut self, address: Address) { + pub async fn run(mut self, address: ProtocolAddr) { let run_channels = self.run_channels.take().unwrap(); self.network.listen(address).await.unwrap(); diff --git a/network/examples/network-speed/src/main.rs b/network/examples/network-speed/src/main.rs index f047983394..91072d1153 100644 --- a/network/examples/network-speed/src/main.rs +++ b/network/examples/network-speed/src/main.rs @@ -7,7 +7,7 @@ mod metrics; use clap::{App, Arg}; use futures::executor::block_on; -use network::{Address, MessageBuffer, Network, Pid, PROMISES_CONSISTENCY, PROMISES_ORDERED}; +use network::{ProtocolAddr, MessageBuffer, Network, Pid, PROMISES_CONSISTENCY, PROMISES_ORDERED}; use serde::{Deserialize, Serialize}; use std::{ sync::Arc, @@ -96,8 +96,8 @@ fn main() { let port: u16 = matches.value_of("port").unwrap().parse().unwrap(); let ip: &str = matches.value_of("ip").unwrap(); let address = match matches.value_of("protocol") { - Some("tcp") => Address::Tcp(format!("{}:{}", ip, port).parse().unwrap()), - Some("udp") => Address::Udp(format!("{}:{}", ip, port).parse().unwrap()), + Some("tcp") => ProtocolAddr::Tcp(format!("{}:{}", ip, port).parse().unwrap()), + Some("udp") => ProtocolAddr::Udp(format!("{}:{}", ip, port).parse().unwrap()), _ => panic!("Invalid mode, run --help!"), }; @@ -118,7 +118,7 @@ fn main() { } } -fn server(address: Address) { +fn server(address: ProtocolAddr) { let mut metrics = metrics::SimpleMetrics::new(); let (server, f) = Network::new(Pid::new(), Some(metrics.registry())); std::thread::spawn(f); @@ -146,7 +146,7 @@ fn server(address: Address) { } } -fn client(address: Address) { +fn client(address: ProtocolAddr) { let mut metrics = metrics::SimpleMetrics::new(); let (client, f) = Network::new(Pid::new(), Some(metrics.registry())); std::thread::spawn(f); diff --git a/network/src/api.rs b/network/src/api.rs index 8ef1d0918c..b9306d595c 100644 --- a/network/src/api.rs +++ b/network/src/api.rs @@ -31,7 +31,7 @@ type ParticipantCloseChannel = /// Represents a Tcp or Udp or Mpsc address #[derive(Clone, Debug, Hash, PartialEq, Eq)] -pub enum Address { +pub enum ProtocolAddr { Tcp(SocketAddr), Udp(SocketAddr), Mpsc(u64), @@ -111,12 +111,12 @@ pub enum StreamError { /// [`Arc`](std::sync::Arc) as all commands have internal mutability. /// /// The `Network` has methods to [`connect`] to other [`Participants`] actively -/// via their [`Address`], or [`listen`] passively for [`connected`] +/// via their [`ProtocolAddr`], or [`listen`] passively for [`connected`] /// [`Participants`]. /// /// # Examples /// ```rust -/// use veloren_network::{Network, Address, Pid}; +/// use veloren_network::{Network, ProtocolAddr, Pid}; /// use futures::executor::block_on; /// /// # fn main() -> std::result::Result<(), Box> { @@ -127,9 +127,9 @@ pub enum StreamError { /// # //setup pseudo database! /// # let (database, fd) = Network::new(Pid::new(), None); /// # std::thread::spawn(fd); -/// # database.listen(Address::Tcp("127.0.0.1:8080".parse().unwrap())).await?; -/// network.listen(Address::Tcp("127.0.0.1:2999".parse().unwrap())).await?; -/// let database = network.connect(Address::Tcp("127.0.0.1:8080".parse().unwrap())).await?; +/// # database.listen(ProtocolAddr::Tcp("127.0.0.1:8080".parse().unwrap())).await?; +/// network.listen(ProtocolAddr::Tcp("127.0.0.1:2999".parse().unwrap())).await?; +/// let database = network.connect(ProtocolAddr::Tcp("127.0.0.1:8080".parse().unwrap())).await?; /// # Ok(()) /// }) /// # } @@ -144,9 +144,9 @@ pub struct Network { participant_disconnect_sender: RwLock>>>>, listen_sender: - RwLock>)>>, + RwLock>)>>, connect_sender: - RwLock>)>>, + RwLock>)>>, connected_receiver: RwLock>, shutdown_sender: Option>, } @@ -176,7 +176,7 @@ impl Network { /// ```rust /// //Example with uvth /// use uvth::ThreadPoolBuilder; - /// use veloren_network::{Address, Network, Pid}; + /// use veloren_network::{Network, Pid, ProtocolAddr}; /// /// let pool = ThreadPoolBuilder::new().build(); /// let (network, f) = Network::new(Pid::new(), None); @@ -185,7 +185,7 @@ impl Network { /// /// ```rust /// //Example with std::thread - /// use veloren_network::{Address, Network, Pid}; + /// use veloren_network::{Network, Pid, ProtocolAddr}; /// /// let (network, f) = Network::new(Pid::new(), None); /// std::thread::spawn(f); @@ -228,7 +228,7 @@ impl Network { ) } - /// starts listening on an [`Address`]. + /// starts listening on an [`ProtocolAddr`]. /// When the method returns the `Network` is ready to listen for incoming /// connections OR has returned a [`NetworkError`] (e.g. port already used). /// You can call [`connected`] to asynchrony wait for a [`Participant`] to @@ -238,7 +238,7 @@ impl Network { /// # Examples /// ```rust /// use futures::executor::block_on; - /// use veloren_network::{Address, Network, Pid}; + /// use veloren_network::{Network, Pid, ProtocolAddr}; /// /// # fn main() -> std::result::Result<(), Box> { /// // Create a Network, listen on port `2000` TCP on all NICs and `2001` UDP locally @@ -246,10 +246,10 @@ impl Network { /// std::thread::spawn(f); /// block_on(async { /// network - /// .listen(Address::Tcp("0.0.0.0:2000".parse().unwrap())) + /// .listen(ProtocolAddr::Tcp("0.0.0.0:2000".parse().unwrap())) /// .await?; /// network - /// .listen(Address::Udp("127.0.0.1:2001".parse().unwrap())) + /// .listen(ProtocolAddr::Udp("127.0.0.1:2001".parse().unwrap())) /// .await?; /// # Ok(()) /// }) @@ -257,7 +257,7 @@ impl Network { /// ``` /// /// [`connected`]: Network::connected - pub async fn listen(&self, address: Address) -> Result<(), NetworkError> { + pub async fn listen(&self, address: ProtocolAddr) -> Result<(), NetworkError> { let (s2a_result_s, s2a_result_r) = oneshot::channel::>(); debug!(?address, "listening on address"); self.listen_sender @@ -273,13 +273,13 @@ impl Network { } } - /// starts connectiong to an [`Address`]. + /// starts connectiong to an [`ProtocolAddr`]. /// When the method returns the Network either returns a [`Participant`] /// ready to open [`Streams`] on OR has returned a [`NetworkError`] (e.g. /// can't connect, or invalid Handshake) # Examples /// ```rust /// use futures::executor::block_on; - /// use veloren_network::{Address, Network, Pid}; + /// use veloren_network::{Network, Pid, ProtocolAddr}; /// /// # fn main() -> std::result::Result<(), Box> { /// // Create a Network, connect on port `2010` TCP and `2011` UDP like listening above @@ -288,16 +288,16 @@ impl Network { /// # let (remote, fr) = Network::new(Pid::new(), None); /// # std::thread::spawn(fr); /// block_on(async { - /// # remote.listen(Address::Tcp("0.0.0.0:2010".parse().unwrap())).await?; - /// # remote.listen(Address::Udp("0.0.0.0:2011".parse().unwrap())).await?; + /// # remote.listen(ProtocolAddr::Tcp("0.0.0.0:2010".parse().unwrap())).await?; + /// # remote.listen(ProtocolAddr::Udp("0.0.0.0:2011".parse().unwrap())).await?; /// let p1 = network - /// .connect(Address::Tcp("127.0.0.1:2010".parse().unwrap())) + /// .connect(ProtocolAddr::Tcp("127.0.0.1:2010".parse().unwrap())) /// .await?; /// # //this doesn't work yet, so skip the test /// # //TODO fixme! /// # return Ok(()); /// let p2 = network - /// .connect(Address::Udp("127.0.0.1:2011".parse().unwrap())) + /// .connect(ProtocolAddr::Udp("127.0.0.1:2011".parse().unwrap())) /// .await?; /// assert_eq!(&p1, &p2); /// # Ok(()) @@ -306,14 +306,14 @@ impl Network { /// ``` /// Usually the `Network` guarantees that a operation on a [`Participant`] /// succeeds, e.g. by automatic retrying unless it fails completely e.g. by - /// disconnecting from the remote. If 2 [`Addresses`] you `connect` to + /// disconnecting from the remote. If 2 [`ProtocolAddres`] you `connect` to /// belongs to the same [`Participant`], you get the same [`Participant`] as /// a result. This is useful e.g. by connecting to the same /// [`Participant`] via multiple Protocols. /// /// [`Streams`]: crate::api::Stream - /// [`Addresses`]: crate::api::Address - pub async fn connect(&self, address: Address) -> Result { + /// [`ProtocolAddres`]: crate::api::ProtocolAddr + pub async fn connect(&self, address: ProtocolAddr) -> Result { let (pid_sender, pid_receiver) = oneshot::channel::>(); debug!(?address, "Connect to address"); self.connect_sender @@ -337,7 +337,7 @@ impl Network { Ok(participant) } - /// returns a [`Participant`] created from a [`Address`] you called + /// returns a [`Participant`] created from a [`ProtocolAddr`] you called /// [`listen`] on before. This function will either return a working /// [`Participant`] ready to open [`Streams`] on OR has returned a /// [`NetworkError`] (e.g. Network got closed) @@ -345,7 +345,7 @@ impl Network { /// # Examples /// ```rust /// use futures::executor::block_on; - /// use veloren_network::{Address, Network, Pid}; + /// use veloren_network::{Network, Pid, ProtocolAddr}; /// /// # fn main() -> std::result::Result<(), Box> { /// // Create a Network, listen on port `2020` TCP and opens returns their Pid @@ -355,9 +355,9 @@ impl Network { /// # std::thread::spawn(fr); /// block_on(async { /// network - /// .listen(Address::Tcp("0.0.0.0:2020".parse().unwrap())) + /// .listen(ProtocolAddr::Tcp("0.0.0.0:2020".parse().unwrap())) /// .await?; - /// # remote.connect(Address::Tcp("0.0.0.0:2020".parse().unwrap())).await?; + /// # remote.connect(ProtocolAddr::Tcp("0.0.0.0:2020".parse().unwrap())).await?; /// while let Ok(participant) = network.connected().await { /// println!("Participant connected: {}", participant.remote_pid()); /// # //skip test here as it would be a endless loop @@ -418,7 +418,7 @@ impl Participant { /// # Examples /// ```rust /// use futures::executor::block_on; - /// use veloren_network::{Address, Network, Pid, PROMISES_CONSISTENCY, PROMISES_ORDERED}; + /// use veloren_network::{Network, Pid, ProtocolAddr, PROMISES_CONSISTENCY, PROMISES_ORDERED}; /// /// # fn main() -> std::result::Result<(), Box> { /// // Create a Network, connect on port 2100 and open a stream @@ -427,9 +427,9 @@ impl Participant { /// # let (remote, fr) = Network::new(Pid::new(), None); /// # std::thread::spawn(fr); /// block_on(async { - /// # remote.listen(Address::Tcp("0.0.0.0:2100".parse().unwrap())).await?; + /// # remote.listen(ProtocolAddr::Tcp("0.0.0.0:2100".parse().unwrap())).await?; /// let p1 = network - /// .connect(Address::Tcp("127.0.0.1:2100".parse().unwrap())) + /// .connect(ProtocolAddr::Tcp("127.0.0.1:2100".parse().unwrap())) /// .await?; /// let _s1 = p1.open(16, PROMISES_ORDERED | PROMISES_CONSISTENCY).await?; /// # Ok(()) @@ -479,7 +479,7 @@ impl Participant { /// /// # Examples /// ```rust - /// use veloren_network::{Network, Pid, Address, PROMISES_ORDERED, PROMISES_CONSISTENCY}; + /// use veloren_network::{Network, Pid, ProtocolAddr, PROMISES_ORDERED, PROMISES_CONSISTENCY}; /// use futures::executor::block_on; /// /// # fn main() -> std::result::Result<(), Box> { @@ -490,8 +490,8 @@ impl Participant { /// # let (remote, fr) = Network::new(Pid::new(), None); /// # std::thread::spawn(fr); /// block_on(async { - /// # remote.listen(Address::Tcp("0.0.0.0:2110".parse().unwrap())).await?; - /// let p1 = network.connect(Address::Tcp("127.0.0.1:2110".parse().unwrap())).await?; + /// # remote.listen(ProtocolAddr::Tcp("0.0.0.0:2110".parse().unwrap())).await?; + /// let p1 = network.connect(ProtocolAddr::Tcp("127.0.0.1:2110".parse().unwrap())).await?; /// # let p2 = remote.connected().await?; /// # p2.open(16, PROMISES_ORDERED | PROMISES_CONSISTENCY).await?; /// let _s1 = p1.opened().await?; @@ -543,7 +543,7 @@ impl Participant { /// # Examples /// ```rust /// use futures::executor::block_on; - /// use veloren_network::{Address, Network, Pid}; + /// use veloren_network::{Network, Pid, ProtocolAddr}; /// /// # fn main() -> std::result::Result<(), Box> { /// // Create a Network, listen on port `2030` TCP and opens returns their Pid and close connection. @@ -553,9 +553,9 @@ impl Participant { /// # std::thread::spawn(fr); /// block_on(async { /// network - /// .listen(Address::Tcp("0.0.0.0:2030".parse().unwrap())) + /// .listen(ProtocolAddr::Tcp("0.0.0.0:2030".parse().unwrap())) /// .await?; - /// # remote.connect(Address::Tcp("0.0.0.0:2030".parse().unwrap())).await?; + /// # remote.connect(ProtocolAddr::Tcp("0.0.0.0:2030".parse().unwrap())).await?; /// while let Ok(participant) = network.connected().await { /// println!("Participant connected: {}", participant.remote_pid()); /// participant.disconnect().await?; @@ -674,7 +674,7 @@ impl Stream { /// /// # Example /// ``` - /// use veloren_network::{Network, Address, Pid}; + /// use veloren_network::{Network, ProtocolAddr, Pid}; /// # use veloren_network::{PROMISES_ORDERED, PROMISES_CONSISTENCY}; /// use futures::executor::block_on; /// @@ -685,8 +685,8 @@ impl Stream { /// # let (remote, fr) = Network::new(Pid::new(), None); /// # std::thread::spawn(fr); /// block_on(async { - /// network.listen(Address::Tcp("127.0.0.1:2200".parse().unwrap())).await?; - /// # let remote_p = remote.connect(Address::Tcp("127.0.0.1:2200".parse().unwrap())).await?; + /// network.listen(ProtocolAddr::Tcp("127.0.0.1:2200".parse().unwrap())).await?; + /// # let remote_p = remote.connect(ProtocolAddr::Tcp("127.0.0.1:2200".parse().unwrap())).await?; /// # // keep it alive /// # let _stream_p = remote_p.open(16, PROMISES_ORDERED | PROMISES_CONSISTENCY).await?; /// let participant_a = network.connected().await?; @@ -713,7 +713,7 @@ impl Stream { /// /// # Example /// ```rust - /// use veloren_network::{Network, Address, Pid, MessageBuffer}; + /// use veloren_network::{Network, ProtocolAddr, Pid, MessageBuffer}; /// # use veloren_network::{PROMISES_ORDERED, PROMISES_CONSISTENCY}; /// use futures::executor::block_on; /// use bincode; @@ -727,9 +727,9 @@ impl Stream { /// # let (remote2, fr2) = Network::new(Pid::new(), None); /// # std::thread::spawn(fr2); /// block_on(async { - /// network.listen(Address::Tcp("127.0.0.1:2210".parse().unwrap())).await?; - /// # let remote1_p = remote1.connect(Address::Tcp("127.0.0.1:2210".parse().unwrap())).await?; - /// # let remote2_p = remote2.connect(Address::Tcp("127.0.0.1:2210".parse().unwrap())).await?; + /// network.listen(ProtocolAddr::Tcp("127.0.0.1:2210".parse().unwrap())).await?; + /// # let remote1_p = remote1.connect(ProtocolAddr::Tcp("127.0.0.1:2210".parse().unwrap())).await?; + /// # let remote2_p = remote2.connect(ProtocolAddr::Tcp("127.0.0.1:2210".parse().unwrap())).await?; /// # assert_eq!(remote1_p.remote_pid(), remote2_p.remote_pid()); /// # remote1_p.open(16, PROMISES_ORDERED | PROMISES_CONSISTENCY).await?; /// # remote2_p.open(16, PROMISES_ORDERED | PROMISES_CONSISTENCY).await?; @@ -779,7 +779,7 @@ impl Stream { /// /// # Example /// ``` - /// use veloren_network::{Network, Address, Pid}; + /// use veloren_network::{Network, ProtocolAddr, Pid}; /// # use veloren_network::{PROMISES_ORDERED, PROMISES_CONSISTENCY}; /// use futures::executor::block_on; /// @@ -790,8 +790,8 @@ impl Stream { /// # let (remote, fr) = Network::new(Pid::new(), None); /// # std::thread::spawn(fr); /// block_on(async { - /// network.listen(Address::Tcp("127.0.0.1:2220".parse().unwrap())).await?; - /// # let remote_p = remote.connect(Address::Tcp("127.0.0.1:2220".parse().unwrap())).await?; + /// network.listen(ProtocolAddr::Tcp("127.0.0.1:2220".parse().unwrap())).await?; + /// # let remote_p = remote.connect(ProtocolAddr::Tcp("127.0.0.1:2220".parse().unwrap())).await?; /// # let mut stream_p = remote_p.open(16, PROMISES_ORDERED | PROMISES_CONSISTENCY).await?; /// # stream_p.send("Hello World"); /// let participant_a = network.connected().await?; diff --git a/network/src/lib.rs b/network/src/lib.rs index 2cbb2bc494..7df6ffe8b6 100644 --- a/network/src/lib.rs +++ b/network/src/lib.rs @@ -14,16 +14,16 @@ //! struct [`Network`] once with a new [`Pid`]. The Pid is necessary to identify //! other [`Networks`] over the network protocols (e.g. TCP, UDP) //! -//! To connect to another application, you must know it's [`Address`]. One side -//! will call [`connect`], the other [`connected`]. If successfull both +//! To connect to another application, you must know it's [`ProtocolAddr`]. One +//! side will call [`connect`], the other [`connected`]. If successfull both //! applications will now get a [`Participant`]. //! //! This [`Participant`] represents the connection between those 2 applications. -//! over the respective [`Address`] and with it the choosen network protocol. -//! However messages can't be send directly via [`Participants`], instead you -//! must open a [`Stream`] on it. Like above, one side has to call [`open`], the -//! other [`opened`]. [`Streams`] can have a different priority and -//! [`Promises`]. +//! over the respective [`ProtocolAddr`] and with it the choosen network +//! protocol. However messages can't be send directly via [`Participants`], +//! instead you must open a [`Stream`] on it. Like above, one side has to call +//! [`open`], the other [`opened`]. [`Streams`] can have a different priority +//! and [`Promises`]. //! //! You can now use the [`Stream`] to [`send`] and [`recv`] in both directions. //! You can send all kind of messages that implement [`serde`]. @@ -40,7 +40,7 @@ //! ```rust //! use async_std::task::sleep; //! use futures::{executor::block_on, join}; -//! use veloren_network::{Address, Network, Pid, PROMISES_CONSISTENCY, PROMISES_ORDERED}; +//! use veloren_network::{Network, Pid, ProtocolAddr, PROMISES_CONSISTENCY, PROMISES_ORDERED}; //! //! // Client //! async fn client() -> std::result::Result<(), Box> { @@ -48,7 +48,7 @@ //! let (client_network, f) = Network::new(Pid::new(), None); //! std::thread::spawn(f); //! let server = client_network -//! .connect(Address::Tcp("127.0.0.1:12345".parse().unwrap())) +//! .connect(ProtocolAddr::Tcp("127.0.0.1:12345".parse().unwrap())) //! .await?; //! let mut stream = server //! .open(10, PROMISES_ORDERED | PROMISES_CONSISTENCY) @@ -62,7 +62,7 @@ //! let (server_network, f) = Network::new(Pid::new(), None); //! std::thread::spawn(f); //! server_network -//! .listen(Address::Tcp("127.0.0.1:12345".parse().unwrap())) +//! .listen(ProtocolAddr::Tcp("127.0.0.1:12345".parse().unwrap())) //! .await?; //! let client = server_network.connected().await?; //! let mut stream = client.opened().await?; @@ -95,7 +95,7 @@ //! [`send`]: crate::api::Stream::send //! [`recv`]: crate::api::Stream::recv //! [`Pid`]: crate::types::Pid -//! [`Address`]: crate::api::Address +//! [`ProtocolAddr`]: crate::api::ProtocolAddr //! [`Promises`]: crate::types::Promises mod api; @@ -109,7 +109,9 @@ mod scheduler; #[macro_use] mod types; -pub use api::{Address, Network, NetworkError, Participant, ParticipantError, Stream, StreamError}; +pub use api::{ + Network, NetworkError, Participant, ParticipantError, ProtocolAddr, Stream, StreamError, +}; pub use message::MessageBuffer; pub use types::{ Pid, Promises, PROMISES_COMPRESSED, PROMISES_CONSISTENCY, PROMISES_ENCRYPTED, diff --git a/network/src/message.rs b/network/src/message.rs index 24b55bb21e..baf6ef6ce1 100644 --- a/network/src/message.rs +++ b/network/src/message.rs @@ -1,6 +1,6 @@ use serde::{de::DeserializeOwned, Serialize}; //use std::collections::VecDeque; -use crate::types::{Mid, Sid}; +use crate::types::{Frame, Mid, Sid}; use std::{io, sync::Arc}; //Todo: Evaluate switching to VecDeque for quickly adding and removing data @@ -52,6 +52,38 @@ pub(crate) fn deserialize(buffer: MessageBuffer) -> bincode bincode::deserialize(span.as_slice()) } +impl OutgoingMessage { + pub(crate) const FRAME_DATA_SIZE: u64 = 1400; + + /// returns if msg is empty + pub(crate) fn fill_next>( + &mut self, + msg_sid: Sid, + frames: &mut E, + ) -> bool { + let to_send = std::cmp::min( + self.buffer.data[self.cursor as usize..].len() as u64, + Self::FRAME_DATA_SIZE, + ); + if to_send > 0 { + if self.cursor == 0 { + frames.extend(std::iter::once((msg_sid, Frame::DataHeader { + mid: self.mid, + sid: self.sid, + length: self.buffer.data.len() as u64, + }))); + } + frames.extend(std::iter::once((msg_sid, Frame::Data { + mid: self.mid, + start: self.cursor, + data: self.buffer.data[self.cursor as usize..][..to_send as usize].to_vec(), + }))); + }; + self.cursor += to_send; + self.cursor >= self.buffer.data.len() as u64 + } +} + ///wouldn't trust this aaaassss much, fine for tests pub(crate) fn partial_eq_io_error(first: &io::Error, second: &io::Error) -> bool { if let Some(f) = first.raw_os_error() { diff --git a/network/src/prios.rs b/network/src/prios.rs index fbbad553e9..83905a06dd 100644 --- a/network/src/prios.rs +++ b/network/src/prios.rs @@ -40,7 +40,6 @@ pub(crate) struct PrioManager { } impl PrioManager { - const FRAME_DATA_SIZE: u64 = 1400; const PRIOS: [u32; PRIO_MAX] = [ 100, 115, 132, 152, 174, 200, 230, 264, 303, 348, 400, 459, 528, 606, 696, 800, 919, 1056, 1213, 1393, 1600, 1838, 2111, 2425, 2786, 3200, 3676, 4222, 4850, 5572, 6400, 7352, 8445, @@ -201,34 +200,6 @@ impl PrioManager { .min_by_key(|&n| self.points[*n as usize]).cloned()*/ } - /// returns if msg is empty - fn tick_msg>( - msg: &mut OutgoingMessage, - msg_sid: Sid, - frames: &mut E, - ) -> bool { - let to_send = std::cmp::min( - msg.buffer.data[msg.cursor as usize..].len() as u64, - Self::FRAME_DATA_SIZE, - ); - if to_send > 0 { - if msg.cursor == 0 { - frames.extend(std::iter::once((msg_sid, Frame::DataHeader { - mid: msg.mid, - sid: msg.sid, - length: msg.buffer.data.len() as u64, - }))); - } - frames.extend(std::iter::once((msg_sid, Frame::Data { - mid: msg.mid, - start: msg.cursor, - data: msg.buffer.data[msg.cursor as usize..][..to_send as usize].to_vec(), - }))); - }; - msg.cursor += to_send; - msg.cursor >= msg.buffer.data.len() as u64 - } - /// no_of_frames = frames.len() /// Your goal is to try to find a realistic no_of_frames! /// no_of_frames should be choosen so, that all Frames can be send out till @@ -257,7 +228,7 @@ impl PrioManager { // => messages with same prio get a fair chance :) //TODO: evalaute not poping every time let (sid, mut msg) = self.messages[prio as usize].pop_front().unwrap(); - if Self::tick_msg(&mut msg, sid, frames) { + if msg.fill_next(sid, frames) { //trace!(?m.mid, "finish message"); //check if prio is empty if self.messages[prio as usize].is_empty() { @@ -314,8 +285,8 @@ mod tests { use futures::{channel::oneshot, executor::block_on}; use std::{collections::VecDeque, sync::Arc}; - const SIZE: u64 = PrioManager::FRAME_DATA_SIZE; - const USIZE: usize = PrioManager::FRAME_DATA_SIZE as usize; + const SIZE: u64 = OutgoingMessage::FRAME_DATA_SIZE; + const USIZE: usize = OutgoingMessage::FRAME_DATA_SIZE as usize; #[allow(clippy::type_complexity)] fn mock_new() -> ( diff --git a/network/src/protocols.rs b/network/src/protocols.rs index 801e227d85..8dde51fc31 100644 --- a/network/src/protocols.rs +++ b/network/src/protocols.rs @@ -14,7 +14,7 @@ use futures::{ sink::SinkExt, stream::StreamExt, }; -use std::{net::SocketAddr, sync::Arc}; +use std::{convert::TryFrom, net::SocketAddr, sync::Arc}; use tracing::*; // Reserving bytes 0, 10, 13 as i have enough space and want to make it easy to @@ -108,15 +108,13 @@ impl TcpProtocol { FRAME_HANDSHAKE => { let mut bytes = [0u8; 19]; Self::read_except_or_close(cid, &stream, &mut bytes, w2c_cid_frame_s).await; - let magic_number = [ - bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], - ]; + let magic_number = *<&[u8; 7]>::try_from(&bytes[0..7]).unwrap(); Frame::Handshake { magic_number, version: [ - u32::from_le_bytes([bytes[7], bytes[8], bytes[9], bytes[10]]), - u32::from_le_bytes([bytes[11], bytes[12], bytes[13], bytes[14]]), - u32::from_le_bytes([bytes[15], bytes[16], bytes[17], bytes[18]]), + u32::from_le_bytes(*<&[u8; 4]>::try_from(&bytes[7..11]).unwrap()), + u32::from_le_bytes(*<&[u8; 4]>::try_from(&bytes[11..15]).unwrap()), + u32::from_le_bytes(*<&[u8; 4]>::try_from(&bytes[15..19]).unwrap()), ], } }, @@ -124,7 +122,7 @@ impl TcpProtocol { let mut bytes = [0u8; 16]; Self::read_except_or_close(cid, &stream, &mut bytes, w2c_cid_frame_s).await; let pid = Pid::from_le_bytes(bytes); - stream.read_exact(&mut bytes).await.unwrap(); + Self::read_except_or_close(cid, &stream, &mut bytes, w2c_cid_frame_s).await; let secret = u128::from_le_bytes(bytes); Frame::Init { pid, secret } }, @@ -132,10 +130,7 @@ impl TcpProtocol { FRAME_OPEN_STREAM => { let mut bytes = [0u8; 10]; Self::read_except_or_close(cid, &stream, &mut bytes, w2c_cid_frame_s).await; - let sid = Sid::from_le_bytes([ - bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], - bytes[7], - ]); + let sid = Sid::from_le_bytes(*<&[u8; 8]>::try_from(&bytes[0..8]).unwrap()); let prio = bytes[8]; let promises = bytes[9]; Frame::OpenStream { @@ -147,41 +142,23 @@ impl TcpProtocol { FRAME_CLOSE_STREAM => { let mut bytes = [0u8; 8]; Self::read_except_or_close(cid, &stream, &mut bytes, w2c_cid_frame_s).await; - let sid = Sid::from_le_bytes([ - bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], - bytes[7], - ]); + let sid = Sid::from_le_bytes(*<&[u8; 8]>::try_from(&bytes[0..8]).unwrap()); Frame::CloseStream { sid } }, FRAME_DATA_HEADER => { let mut bytes = [0u8; 24]; Self::read_except_or_close(cid, &stream, &mut bytes, w2c_cid_frame_s).await; - let mid = Mid::from_le_bytes([ - bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], - bytes[7], - ]); - let sid = Sid::from_le_bytes([ - bytes[8], bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], - bytes[15], - ]); - let length = u64::from_le_bytes([ - bytes[16], bytes[17], bytes[18], bytes[19], bytes[20], bytes[21], - bytes[22], bytes[23], - ]); + let mid = Mid::from_le_bytes(*<&[u8; 8]>::try_from(&bytes[0..8]).unwrap()); + let sid = Sid::from_le_bytes(*<&[u8; 8]>::try_from(&bytes[8..16]).unwrap()); + let length = u64::from_le_bytes(*<&[u8; 8]>::try_from(&bytes[16..24]).unwrap()); Frame::DataHeader { mid, sid, length } }, FRAME_DATA => { let mut bytes = [0u8; 18]; Self::read_except_or_close(cid, &stream, &mut bytes, w2c_cid_frame_s).await; - let mid = Mid::from_le_bytes([ - bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], - bytes[7], - ]); - let start = u64::from_le_bytes([ - bytes[8], bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], - bytes[15], - ]); - let length = u16::from_le_bytes([bytes[16], bytes[17]]); + let mid = Mid::from_le_bytes(*<&[u8; 8]>::try_from(&bytes[0..8]).unwrap()); + let start = u64::from_le_bytes(*<&[u8; 8]>::try_from(&bytes[8..16]).unwrap()); + let length = u16::from_le_bytes(*<&[u8; 2]>::try_from(&bytes[16..18]).unwrap()); let mut data = vec![0; length as usize]; throughput_cache.inc_by(length as i64); Self::read_except_or_close(cid, &stream, &mut data, w2c_cid_frame_s).await; diff --git a/network/src/scheduler.rs b/network/src/scheduler.rs index 6fd45c7950..6679067048 100644 --- a/network/src/scheduler.rs +++ b/network/src/scheduler.rs @@ -1,5 +1,5 @@ use crate::{ - api::{Address, Participant}, + api::{Participant, ProtocolAddr}, channel::Handshake, metrics::NetworkMetrics, participant::BParticipant, @@ -50,8 +50,9 @@ struct ParticipantInfo { /// - c: channel/handshake #[derive(Debug)] struct ControlChannels { - a2s_listen_r: mpsc::UnboundedReceiver<(Address, oneshot::Sender>)>, - a2s_connect_r: mpsc::UnboundedReceiver<(Address, oneshot::Sender>)>, + a2s_listen_r: mpsc::UnboundedReceiver<(ProtocolAddr, oneshot::Sender>)>, + a2s_connect_r: + mpsc::UnboundedReceiver<(ProtocolAddr, oneshot::Sender>)>, a2s_scheduler_shutdown_r: oneshot::Receiver<()>, a2s_disconnect_r: mpsc::UnboundedReceiver<(Pid, oneshot::Sender>)>, b2s_prio_statistic_r: mpsc::UnboundedReceiver<(Pid, u64, u64)>, @@ -74,7 +75,7 @@ pub struct Scheduler { participant_channels: Arc>>, participants: Arc>>, channel_ids: Arc, - channel_listener: RwLock>>, + channel_listener: RwLock>>, metrics: Arc, } @@ -85,15 +86,15 @@ impl Scheduler { registry: Option<&Registry>, ) -> ( Self, - mpsc::UnboundedSender<(Address, oneshot::Sender>)>, - mpsc::UnboundedSender<(Address, oneshot::Sender>)>, + mpsc::UnboundedSender<(ProtocolAddr, oneshot::Sender>)>, + mpsc::UnboundedSender<(ProtocolAddr, oneshot::Sender>)>, mpsc::UnboundedReceiver, oneshot::Sender<()>, ) { let (a2s_listen_s, a2s_listen_r) = - mpsc::unbounded::<(Address, oneshot::Sender>)>(); + mpsc::unbounded::<(ProtocolAddr, oneshot::Sender>)>(); let (a2s_connect_s, a2s_connect_r) = - mpsc::unbounded::<(Address, oneshot::Sender>)>(); + mpsc::unbounded::<(ProtocolAddr, oneshot::Sender>)>(); let (s2a_connected_s, s2a_connected_r) = mpsc::unbounded::(); let (a2s_scheduler_shutdown_s, a2s_scheduler_shutdown_r) = oneshot::channel::<()>(); let (a2s_disconnect_s, a2s_disconnect_r) = @@ -156,7 +157,7 @@ impl Scheduler { async fn listen_mgr( &self, - a2s_listen_r: mpsc::UnboundedReceiver<(Address, oneshot::Sender>)>, + a2s_listen_r: mpsc::UnboundedReceiver<(ProtocolAddr, oneshot::Sender>)>, ) { trace!("Start listen_mgr"); a2s_listen_r @@ -168,9 +169,9 @@ impl Scheduler { self.metrics .listen_requests_total .with_label_values(&[match address { - Address::Tcp(_) => "tcp", - Address::Udp(_) => "udp", - Address::Mpsc(_) => "mpsc", + ProtocolAddr::Tcp(_) => "tcp", + ProtocolAddr::Udp(_) => "udp", + ProtocolAddr::Mpsc(_) => "mpsc", }]) .inc(); let (end_sender, end_receiver) = oneshot::channel::<()>(); @@ -189,14 +190,14 @@ impl Scheduler { async fn connect_mgr( &self, mut a2s_connect_r: mpsc::UnboundedReceiver<( - Address, + ProtocolAddr, oneshot::Sender>, )>, ) { trace!("Start connect_mgr"); while let Some((addr, pid_sender)) = a2s_connect_r.next().await { let (protocol, handshake) = match addr { - Address::Tcp(addr) => { + ProtocolAddr::Tcp(addr) => { self.metrics .connect_requests_total .with_label_values(&["tcp"]) @@ -214,7 +215,7 @@ impl Scheduler { false, ) }, - Address::Udp(addr) => { + ProtocolAddr::Udp(addr) => { self.metrics .connect_requests_total .with_label_values(&["udp"]) @@ -338,13 +339,13 @@ impl Scheduler { async fn channel_creator( &self, - addr: Address, + addr: ProtocolAddr, s2s_stop_listening_r: oneshot::Receiver<()>, s2a_listen_result_s: oneshot::Sender>, ) { trace!(?addr, "Start up channel creator"); match addr { - Address::Tcp(addr) => { + ProtocolAddr::Tcp(addr) => { let listener = match net::TcpListener::bind(addr).await { Ok(listener) => { s2a_listen_result_s.send(Ok(())).unwrap(); @@ -374,7 +375,7 @@ impl Scheduler { .await; } }, - Address::Udp(addr) => { + ProtocolAddr::Udp(addr) => { let socket = match net::UdpSocket::bind(addr).await { Ok(socket) => { s2a_listen_result_s.send(Ok(())).unwrap(); diff --git a/network/tests/helper.rs b/network/tests/helper.rs index 386f6d94dc..f20d29b3cf 100644 --- a/network/tests/helper.rs +++ b/network/tests/helper.rs @@ -7,7 +7,7 @@ use std::{ }; use tracing::*; use tracing_subscriber::EnvFilter; -use veloren_network::{Address, Network, Participant, Pid, Stream, PROMISES_NONE}; +use veloren_network::{Network, Participant, Pid, ProtocolAddr, Stream, PROMISES_NONE}; #[allow(dead_code)] pub fn setup(tracing: bool, mut sleep: u64) -> (u64, u64) { @@ -47,7 +47,7 @@ pub fn setup(tracing: bool, mut sleep: u64) -> (u64, u64) { #[allow(dead_code)] pub async fn network_participant_stream( - addr: Address, + addr: ProtocolAddr, ) -> (Network, Participant, Stream, Network, Participant, Stream) { let (n_a, f_a) = Network::new(Pid::fake(1), None); std::thread::spawn(f_a); @@ -65,19 +65,19 @@ pub async fn network_participant_stream( } #[allow(dead_code)] -pub fn tcp() -> veloren_network::Address { +pub fn tcp() -> veloren_network::ProtocolAddr { lazy_static! { static ref PORTS: AtomicU16 = AtomicU16::new(5000); } let port = PORTS.fetch_add(1, Ordering::Relaxed); - veloren_network::Address::Tcp(SocketAddr::from(([127, 0, 0, 1], port))) + veloren_network::ProtocolAddr::Tcp(SocketAddr::from(([127, 0, 0, 1], port))) } #[allow(dead_code)] -pub fn udp() -> veloren_network::Address { +pub fn udp() -> veloren_network::ProtocolAddr { lazy_static! { static ref PORTS: AtomicU16 = AtomicU16::new(5000); } let port = PORTS.fetch_add(1, Ordering::Relaxed); - veloren_network::Address::Udp(SocketAddr::from(([127, 0, 0, 1], port))) + veloren_network::ProtocolAddr::Udp(SocketAddr::from(([127, 0, 0, 1], port))) } diff --git a/network/tests/integration.rs b/network/tests/integration.rs index e0cd43844f..be83cb853b 100644 --- a/network/tests/integration.rs +++ b/network/tests/integration.rs @@ -4,7 +4,7 @@ use veloren_network::{NetworkError, StreamError}; mod helper; use helper::{network_participant_stream, tcp, udp}; use std::io::ErrorKind; -use veloren_network::{Address, Network, Pid, PROMISES_CONSISTENCY, PROMISES_ORDERED}; +use veloren_network::{Network, Pid, ProtocolAddr, PROMISES_CONSISTENCY, PROMISES_ORDERED}; #[test] #[ignore] @@ -68,16 +68,16 @@ fn tcp_and_udp_2_connections() -> std::result::Result<(), Box std::result::Result<(), Box> std::thread::spawn(fr); block_on(async { network - .listen(Address::Tcp("127.0.0.1:1200".parse().unwrap())) + .listen(ProtocolAddr::Tcp("127.0.0.1:1200".parse().unwrap())) .await?; let remote_p = remote - .connect(Address::Tcp("127.0.0.1:1200".parse().unwrap())) + .connect(ProtocolAddr::Tcp("127.0.0.1:1200".parse().unwrap())) .await?; // keep it alive let _stream_p = remote_p @@ -154,10 +154,10 @@ fn api_stream_recv_main() -> std::result::Result<(), Box> std::thread::spawn(fr); block_on(async { network - .listen(Address::Tcp("127.0.0.1:1220".parse().unwrap())) + .listen(ProtocolAddr::Tcp("127.0.0.1:1220".parse().unwrap())) .await?; let remote_p = remote - .connect(Address::Tcp("127.0.0.1:1220".parse().unwrap())) + .connect(ProtocolAddr::Tcp("127.0.0.1:1220".parse().unwrap())) .await?; let mut stream_p = remote_p .open(16, PROMISES_ORDERED | PROMISES_CONSISTENCY) diff --git a/server/src/lib.rs b/server/src/lib.rs index d0943b2d12..9e104e3483 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -41,7 +41,7 @@ use futures_executor::block_on; use futures_timer::Delay; use futures_util::{select, FutureExt}; use metrics::{ServerMetrics, TickMetrics}; -use network::{Address, Network, Pid}; +use network::{Network, Pid, ProtocolAddr}; use persistence::character::{CharacterLoader, CharacterLoaderResponseType, CharacterUpdater}; use specs::{join::Join, Builder, Entity as EcsEntity, RunNow, SystemData, WorldExt}; use std::{ @@ -241,7 +241,7 @@ impl Server { .build(); let (network, f) = Network::new(Pid::new(), None); thread_pool.execute(f); - block_on(network.listen(Address::Tcp(settings.gameserver_address)))?; + block_on(network.listen(ProtocolAddr::Tcp(settings.gameserver_address)))?; let this = Self { state, diff --git a/voxygen/src/menu/main/client_init.rs b/voxygen/src/menu/main/client_init.rs index c11d848c83..05e6281e58 100644 --- a/voxygen/src/menu/main/client_init.rs +++ b/voxygen/src/menu/main/client_init.rs @@ -12,7 +12,7 @@ use std::{ thread, time::Duration, }; -use tracing::debug; +use tracing::{debug, trace, warn}; #[derive(Debug)] pub enum Error { @@ -81,7 +81,7 @@ impl ClientInit { { match Client::new(socket_addr, view_distance) { Ok(mut client) => { - if let Err(err) = + if let Err(e) = client.register(username, password, |auth_server| { let _ = tx .send(Msg::IsAuthTrusted(auth_server.to_string())); @@ -93,29 +93,35 @@ impl ClientInit { .unwrap_or(false) }) { - last_err = Some(Error::ClientError(err)); + last_err = Some(Error::ClientError(e)); break 'tries; } let _ = tx.send(Msg::Done(Ok(client))); return; }, - Err(err) => { - match err { - ClientError::NetworkErr(NetworkError::ConnectFailed( - .., - )) => { - debug!( - "can't reach the server, going to retry in a few \ - seconds" - ); - }, - // Non-connection error, stop attempts - err => { - last_err = Some(Error::ClientError(err)); - break 'tries; - }, + Err(ClientError::NetworkErr(NetworkError::ConnectFailed(e))) => { + if e.kind() == std::io::ErrorKind::PermissionDenied { + warn!( + ?e, + "You can't connect to the server, you are running a \ + incompatible version than the server" + ); + last_err = Some(Error::ClientError( + ClientError::NetworkErr(NetworkError::ConnectFailed(e)), + )); + break 'tries; + } else { + debug!( + "can't reach the server, going to retry in a few \ + seconds" + ); } }, + Err(e) => { + trace!(?e, "stopping connecting to server, due to error"); + last_err = Some(Error::ClientError(e)); + break 'tries; + }, } } thread::sleep(Duration::from_secs(5)); From df45d35c0ece7eb81e72d25f4cc5c6fcd782cb89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=A4rtens?= Date: Fri, 10 Jul 2020 15:31:26 +0200 Subject: [PATCH 23/85] tcp protocol hardening - make it harder for the server to crash and also kill invalid sessions properly (instead of waiting for them to close) - introduce macros to reduce code duplication - added tests to check for valid handshake as well as garbage tcp --- network/src/api.rs | 25 +-- network/src/channel.rs | 53 ++++-- network/src/protocols.rs | 362 ++++++++++++++++++++++---------------- network/src/scheduler.rs | 18 +- server/src/lib.rs | 7 +- server/src/sys/message.rs | 4 +- 6 files changed, 279 insertions(+), 190 deletions(-) diff --git a/network/src/api.rs b/network/src/api.rs index b9306d595c..71ebc3c86b 100644 --- a/network/src/api.rs +++ b/network/src/api.rs @@ -7,7 +7,11 @@ use crate::{ scheduler::Scheduler, types::{Mid, Pid, Prio, Promises, Sid}, }; -use async_std::{io, sync::RwLock, task}; +use async_std::{ + io, + sync::{Mutex, RwLock}, + task, +}; use futures::{ channel::{mpsc, oneshot}, sink::SinkExt, @@ -50,9 +54,7 @@ pub struct Participant { a2b_steam_open_s: RwLock)>>, b2a_stream_opened_r: RwLock>, closed: AtomicBool, - //We need a std::Mutex here, the async Mutex requeres a block in `Drop` which can `panic!` - //It's only okay because `disconnect` is the only `fn` accessing it and it consumes self! - a2s_disconnect_s: Arc>>, + a2s_disconnect_s: Arc>>, } /// `Streams` represents a channel to send `n` messages with a certain priority @@ -142,7 +144,7 @@ pub enum StreamError { pub struct Network { local_pid: Pid, participant_disconnect_sender: - RwLock>>>>, + RwLock>>>>, listen_sender: RwLock>)>>, connect_sender: @@ -394,7 +396,7 @@ impl Participant { a2b_steam_open_s: RwLock::new(a2b_steam_open_s), b2a_stream_opened_r: RwLock::new(b2a_stream_opened_r), closed: AtomicBool::new(false), - a2s_disconnect_s: Arc::new(std::sync::Mutex::new(Some(a2s_disconnect_s))), + a2s_disconnect_s: Arc::new(Mutex::new(Some(a2s_disconnect_s))), } } @@ -575,7 +577,7 @@ impl Participant { self.closed.store(true, Ordering::Relaxed); //Streams will be closed by BParticipant - match self.a2s_disconnect_s.lock().unwrap().take() { + match self.a2s_disconnect_s.lock().await.take() { Some(mut a2s_disconnect_s) => { let (finished_sender, finished_receiver) = oneshot::channel(); // Participant is connecting to Scheduler here, not as usual @@ -840,13 +842,12 @@ impl Drop for Network { ); let mut finished_receiver_list = vec![]; task::block_on(async { - // we need to carefully shut down here! as otherwise we might call - // Participant::Drop with a2s_disconnect_s here which would open - // another task::block, which would panic! + // we MUST avoid nested block_on, good that Network::Drop no longer triggers + // Participant::Drop directly but just the BParticipant for (remote_pid, a2s_disconnect_s) in self.participant_disconnect_sender.write().await.drain() { - match a2s_disconnect_s.lock().unwrap().take() { + match a2s_disconnect_s.lock().await.take() { Some(mut a2s_disconnect_s) => { trace!(?remote_pid, "Participants will be closed"); let (finished_sender, finished_receiver) = oneshot::channel(); @@ -893,7 +894,7 @@ impl Drop for Participant { let pid = self.remote_pid; debug!(?pid, "Shutting down Participant"); - match self.a2s_disconnect_s.lock().unwrap().take() { + match task::block_on(self.a2s_disconnect_s.lock()).take() { None => trace!( ?pid, "Participant has been shutdown cleanly, no further waiting is requiered!" diff --git a/network/src/channel.rs b/network/src/channel.rs index 37ce5a4f8b..752dad16cb 100644 --- a/network/src/channel.rs +++ b/network/src/channel.rs @@ -159,7 +159,7 @@ impl Handshake { &self, w2c_cid_frame_r: &mut mpsc::UnboundedReceiver<(Cid, Frame)>, mut c2w_frame_s: mpsc::UnboundedSender, - _read_stop_sender: oneshot::Sender<()>, + read_stop_sender: oneshot::Sender<()>, ) -> Result<(Pid, Sid, u128), ()> { const ERR_S: &str = "Got A Raw Message, these are usually Debug Messages indicating that \ something went wrong on network layer and connection will be closed"; @@ -170,7 +170,7 @@ impl Handshake { self.send_handshake(&mut c2w_frame_s).await; } - match w2c_cid_frame_r.next().await { + let r = match w2c_cid_frame_r.next().await { Some(( _, Frame::Handshake { @@ -198,9 +198,8 @@ impl Handshake { .unwrap(); c2w_frame_s.send(Frame::Shutdown).await.unwrap(); } - return Err(()); - } - if version != VELOREN_NETWORK_VERSION { + Err(()) + } else if version != VELOREN_NETWORK_VERSION { error!(?version, "Connection with wrong network version"); #[cfg(debug_assertions)] { @@ -225,13 +224,15 @@ impl Handshake { .unwrap(); c2w_frame_s.send(Frame::Shutdown {}).await.unwrap(); } - return Err(()); - } - debug!("Handshake completed"); - if self.init_handshake { - self.send_init(&mut c2w_frame_s, &pid_string).await; + Err(()) } else { - self.send_handshake(&mut c2w_frame_s).await; + debug!("Handshake completed"); + if self.init_handshake { + self.send_init(&mut c2w_frame_s, &pid_string).await; + } else { + self.send_handshake(&mut c2w_frame_s).await; + } + Ok(()) } }, Some((_, Frame::Shutdown)) => { @@ -240,7 +241,7 @@ impl Handshake { .frames_in_total .with_label_values(&[&pid_string, &cid_string, "Shutdown"]) .inc(); - return Err(()); + Err(()) }, Some((_, Frame::Raw(bytes))) => { self.metrics @@ -251,19 +252,29 @@ impl Handshake { Ok(string) => error!(?string, ERR_S), _ => error!(?bytes, ERR_S), } - return Err(()); + Err(()) }, Some((_, frame)) => { self.metrics .frames_in_total .with_label_values(&[&pid_string, &cid_string, frame.get_string()]) .inc(); - return Err(()); + Err(()) }, - None => return Err(()), + None => Err(()), }; + if let Err(()) = r { + if let Err(e) = read_stop_sender.send(()) { + trace!( + ?e, + "couldn't stop protocol, probably it encountered a Protocol Stop and closed \ + itself already, which is fine" + ); + } + return Err(()); + } - match w2c_cid_frame_r.next().await { + let r = match w2c_cid_frame_r.next().await { Some((_, Frame::Init { pid, secret })) => { debug!(?pid, "Participant send their ID"); pid_string = pid.to_string(); @@ -307,7 +318,17 @@ impl Handshake { Err(()) }, None => Err(()), + }; + if r.is_err() { + if let Err(e) = read_stop_sender.send(()) { + trace!( + ?e, + "couldn't stop protocol, probably it encountered a Protocol Stop and closed \ + itself already, which is fine" + ); + } } + r } async fn send_handshake(&self, c2w_frame_s: &mut mpsc::UnboundedSender) { diff --git a/network/src/protocols.rs b/network/src/protocols.rs index 8dde51fc31..970867517f 100644 --- a/network/src/protocols.rs +++ b/network/src/protocols.rs @@ -8,7 +8,7 @@ use async_std::{ }; use futures::{ channel::{mpsc, oneshot}, - future::FutureExt, + future::{Fuse, FutureExt}, lock::Mutex, select, sink::SinkExt, @@ -59,22 +59,35 @@ impl TcpProtocol { } /// read_except and if it fails, close the protocol - async fn read_except_or_close( + async fn read_or_close( cid: Cid, mut stream: &TcpStream, mut bytes: &mut [u8], w2c_cid_frame_s: &mut mpsc::UnboundedSender<(Cid, Frame)>, - ) { - if let Err(e) = stream.read_exact(&mut bytes).await { - warn!( - ?e, - "Closing tcp protocol due to read error, sending close frame to gracefully \ - shutdown" - ); - w2c_cid_frame_s - .send((cid, Frame::Shutdown)) - .await - .expect("Channel or Participant seems no longer to exist to be Shutdown"); + mut end_receiver: &mut Fuse>, + ) -> bool { + match select! { + r = stream.read_exact(&mut bytes).fuse() => Some(r), + _ = end_receiver => None, + } { + Some(Ok(_)) => false, + Some(Err(e)) => { + debug!( + ?cid, + ?e, + "Closing tcp protocol due to read error, sending close frame to gracefully \ + shutdown" + ); + w2c_cid_frame_s + .send((cid, Frame::Shutdown)) + .await + .expect("Channel or Participant seems no longer to exist to be Shutdown"); + true + }, + None => { + trace!(?cid, "shutdown requested"); + true + }, } } @@ -82,7 +95,7 @@ impl TcpProtocol { &self, cid: Cid, w2c_cid_frame_s: &mut mpsc::UnboundedSender<(Cid, Frame)>, - end_receiver: oneshot::Receiver<()>, + end_r: oneshot::Receiver<()>, ) { trace!("Starting up tcp read()"); let mut metrics_cache = CidFrameCache::new(self.metrics.frames_wire_in_total.clone(), cid); @@ -90,24 +103,28 @@ impl TcpProtocol { .metrics .wire_in_throughput .with_label_values(&[&cid.to_string()]); - let mut stream = self.stream.clone(); - let mut end_receiver = end_receiver.fuse(); + let stream = self.stream.clone(); + let mut end_r = end_r.fuse(); + + macro_rules! read_or_close { + ($x:expr) => { + if TcpProtocol::read_or_close(cid, &stream, $x, w2c_cid_frame_s, &mut end_r).await { + info!("Tcp stream closed, shutting down read"); + break; + } + }; + } loop { - let mut bytes = [0u8; 1]; - let r = select! { - r = stream.read_exact(&mut bytes).fuse() => r, - _ = end_receiver => break, + let frame_no = { + let mut bytes = [0u8; 1]; + read_or_close!(&mut bytes); + bytes[0] }; - if r.is_err() { - info!("Tcp stream closed, shutting down read"); - break; - } - let frame_no = bytes[0]; let frame = match frame_no { FRAME_HANDSHAKE => { let mut bytes = [0u8; 19]; - Self::read_except_or_close(cid, &stream, &mut bytes, w2c_cid_frame_s).await; + read_or_close!(&mut bytes); let magic_number = *<&[u8; 7]>::try_from(&bytes[0..7]).unwrap(); Frame::Handshake { magic_number, @@ -120,16 +137,16 @@ impl TcpProtocol { }, FRAME_INIT => { let mut bytes = [0u8; 16]; - Self::read_except_or_close(cid, &stream, &mut bytes, w2c_cid_frame_s).await; + read_or_close!(&mut bytes); let pid = Pid::from_le_bytes(bytes); - Self::read_except_or_close(cid, &stream, &mut bytes, w2c_cid_frame_s).await; + read_or_close!(&mut bytes); let secret = u128::from_le_bytes(bytes); Frame::Init { pid, secret } }, FRAME_SHUTDOWN => Frame::Shutdown, FRAME_OPEN_STREAM => { let mut bytes = [0u8; 10]; - Self::read_except_or_close(cid, &stream, &mut bytes, w2c_cid_frame_s).await; + read_or_close!(&mut bytes); let sid = Sid::from_le_bytes(*<&[u8; 8]>::try_from(&bytes[0..8]).unwrap()); let prio = bytes[8]; let promises = bytes[9]; @@ -141,13 +158,13 @@ impl TcpProtocol { }, FRAME_CLOSE_STREAM => { let mut bytes = [0u8; 8]; - Self::read_except_or_close(cid, &stream, &mut bytes, w2c_cid_frame_s).await; + read_or_close!(&mut bytes); let sid = Sid::from_le_bytes(*<&[u8; 8]>::try_from(&bytes[0..8]).unwrap()); Frame::CloseStream { sid } }, FRAME_DATA_HEADER => { let mut bytes = [0u8; 24]; - Self::read_except_or_close(cid, &stream, &mut bytes, w2c_cid_frame_s).await; + read_or_close!(&mut bytes); let mid = Mid::from_le_bytes(*<&[u8; 8]>::try_from(&bytes[0..8]).unwrap()); let sid = Sid::from_le_bytes(*<&[u8; 8]>::try_from(&bytes[8..16]).unwrap()); let length = u64::from_le_bytes(*<&[u8; 8]>::try_from(&bytes[16..24]).unwrap()); @@ -155,28 +172,30 @@ impl TcpProtocol { }, FRAME_DATA => { let mut bytes = [0u8; 18]; - Self::read_except_or_close(cid, &stream, &mut bytes, w2c_cid_frame_s).await; + read_or_close!(&mut bytes); let mid = Mid::from_le_bytes(*<&[u8; 8]>::try_from(&bytes[0..8]).unwrap()); let start = u64::from_le_bytes(*<&[u8; 8]>::try_from(&bytes[8..16]).unwrap()); let length = u16::from_le_bytes(*<&[u8; 2]>::try_from(&bytes[16..18]).unwrap()); let mut data = vec![0; length as usize]; throughput_cache.inc_by(length as i64); - Self::read_except_or_close(cid, &stream, &mut data, w2c_cid_frame_s).await; + read_or_close!(&mut data); Frame::Data { mid, start, data } }, FRAME_RAW => { let mut bytes = [0u8; 2]; - Self::read_except_or_close(cid, &stream, &mut bytes, w2c_cid_frame_s).await; + read_or_close!(&mut bytes); let length = u16::from_le_bytes([bytes[0], bytes[1]]); let mut data = vec![0; length as usize]; - Self::read_except_or_close(cid, &stream, &mut data, w2c_cid_frame_s).await; + read_or_close!(&mut data); Frame::Raw(data) }, - _ => { + other => { // report a RAW frame, but cannot rely on the next 2 bytes to be a size. - // guessing 256 bytes, which might help to sort down issues - let mut data = vec![0; 256]; - Self::read_except_or_close(cid, &stream, &mut data, w2c_cid_frame_s).await; + // guessing 32 bytes, which might help to sort down issues + let mut data = vec![0; 32]; + //keep the first byte! + read_or_close!(&mut data[1..]); + data[0] = other; Frame::Raw(data) }, }; @@ -193,24 +212,21 @@ impl TcpProtocol { async fn write_or_close( stream: &mut TcpStream, bytes: &[u8], - to_wire_receiver: &mut mpsc::UnboundedReceiver, + c2w_frame_r: &mut mpsc::UnboundedReceiver, ) -> bool { match stream.write_all(&bytes).await { Err(e) => { - warn!( + debug!( ?e, "Got an error writing to tcp, going to close this channel" ); - to_wire_receiver.close(); + c2w_frame_r.close(); true }, _ => false, } } - //dezerialize here as this is executed in a seperate thread PER channel. - // Limites Throughput per single Receiver but stays in same thread (maybe as its - // in a threadpool) for TCP, UDP and MPSC pub async fn write_to_wire(&self, cid: Cid, mut c2w_frame_r: mpsc::UnboundedReceiver) { trace!("Starting up tcp write()"); let mut stream = self.stream.clone(); @@ -219,139 +235,70 @@ impl TcpProtocol { .metrics .wire_out_throughput .with_label_values(&[&cid.to_string()]); + + macro_rules! write_or_close { + ($x:expr) => { + if TcpProtocol::write_or_close(&mut stream, $x, &mut c2w_frame_r).await { + info!("Tcp stream closed, shutting down write"); + break; + } + }; + } + while let Some(frame) = c2w_frame_r.next().await { metrics_cache.with_label_values(&frame).inc(); - if match frame { + match frame { Frame::Handshake { magic_number, version, } => { - Self::write_or_close( - &mut stream, - &FRAME_HANDSHAKE.to_be_bytes(), - &mut c2w_frame_r, - ) - .await - || Self::write_or_close(&mut stream, &magic_number, &mut c2w_frame_r).await - || Self::write_or_close( - &mut stream, - &version[0].to_le_bytes(), - &mut c2w_frame_r, - ) - .await - || Self::write_or_close( - &mut stream, - &version[1].to_le_bytes(), - &mut c2w_frame_r, - ) - .await - || Self::write_or_close( - &mut stream, - &version[2].to_le_bytes(), - &mut c2w_frame_r, - ) - .await + write_or_close!(&FRAME_HANDSHAKE.to_be_bytes()); + write_or_close!(&magic_number); + write_or_close!(&version[0].to_le_bytes()); + write_or_close!(&version[1].to_le_bytes()); + write_or_close!(&version[2].to_le_bytes()); }, Frame::Init { pid, secret } => { - Self::write_or_close(&mut stream, &FRAME_INIT.to_be_bytes(), &mut c2w_frame_r) - .await - || Self::write_or_close(&mut stream, &pid.to_le_bytes(), &mut c2w_frame_r) - .await - || Self::write_or_close( - &mut stream, - &secret.to_le_bytes(), - &mut c2w_frame_r, - ) - .await + write_or_close!(&FRAME_INIT.to_be_bytes()); + write_or_close!(&pid.to_le_bytes()); + write_or_close!(&secret.to_le_bytes()); }, Frame::Shutdown => { - Self::write_or_close( - &mut stream, - &FRAME_SHUTDOWN.to_be_bytes(), - &mut c2w_frame_r, - ) - .await + write_or_close!(&FRAME_SHUTDOWN.to_be_bytes()); }, Frame::OpenStream { sid, prio, promises, } => { - Self::write_or_close( - &mut stream, - &FRAME_OPEN_STREAM.to_be_bytes(), - &mut c2w_frame_r, - ) - .await - || Self::write_or_close(&mut stream, &sid.to_le_bytes(), &mut c2w_frame_r) - .await - || Self::write_or_close(&mut stream, &prio.to_le_bytes(), &mut c2w_frame_r) - .await - || Self::write_or_close( - &mut stream, - &promises.to_le_bytes(), - &mut c2w_frame_r, - ) - .await + write_or_close!(&FRAME_OPEN_STREAM.to_be_bytes()); + write_or_close!(&sid.to_le_bytes()); + write_or_close!(&prio.to_le_bytes()); + write_or_close!(&promises.to_le_bytes()); }, Frame::CloseStream { sid } => { - Self::write_or_close( - &mut stream, - &FRAME_CLOSE_STREAM.to_be_bytes(), - &mut c2w_frame_r, - ) - .await - || Self::write_or_close(&mut stream, &sid.to_le_bytes(), &mut c2w_frame_r) - .await + write_or_close!(&FRAME_CLOSE_STREAM.to_be_bytes()); + write_or_close!(&sid.to_le_bytes()); }, Frame::DataHeader { mid, sid, length } => { - Self::write_or_close( - &mut stream, - &FRAME_DATA_HEADER.to_be_bytes(), - &mut c2w_frame_r, - ) - .await - || Self::write_or_close(&mut stream, &mid.to_le_bytes(), &mut c2w_frame_r) - .await - || Self::write_or_close(&mut stream, &sid.to_le_bytes(), &mut c2w_frame_r) - .await - || Self::write_or_close( - &mut stream, - &length.to_le_bytes(), - &mut c2w_frame_r, - ) - .await + write_or_close!(&FRAME_DATA_HEADER.to_be_bytes()); + write_or_close!(&mid.to_le_bytes()); + write_or_close!(&sid.to_le_bytes()); + write_or_close!(&length.to_le_bytes()); }, Frame::Data { mid, start, data } => { throughput_cache.inc_by(data.len() as i64); - Self::write_or_close(&mut stream, &FRAME_DATA.to_be_bytes(), &mut c2w_frame_r) - .await - || Self::write_or_close(&mut stream, &mid.to_le_bytes(), &mut c2w_frame_r) - .await - || Self::write_or_close(&mut stream, &start.to_le_bytes(), &mut c2w_frame_r) - .await - || Self::write_or_close( - &mut stream, - &(data.len() as u16).to_le_bytes(), - &mut c2w_frame_r, - ) - .await - || Self::write_or_close(&mut stream, &data, &mut c2w_frame_r).await + write_or_close!(&FRAME_DATA.to_be_bytes()); + write_or_close!(&mid.to_le_bytes()); + write_or_close!(&start.to_le_bytes()); + write_or_close!(&(data.len() as u16).to_le_bytes()); + write_or_close!(&data); }, Frame::Raw(data) => { - Self::write_or_close(&mut stream, &FRAME_RAW.to_be_bytes(), &mut c2w_frame_r) - .await - || Self::write_or_close( - &mut stream, - &(data.len() as u16).to_le_bytes(), - &mut c2w_frame_r, - ) - .await - || Self::write_or_close(&mut stream, &data, &mut c2w_frame_r).await + write_or_close!(&FRAME_RAW.to_be_bytes()); + write_or_close!(&(data.len() as u16).to_le_bytes()); + write_or_close!(&data); }, - } { - //failure - return; } } trace!("shutting down tcp write()"); @@ -377,7 +324,7 @@ impl UdpProtocol { &self, cid: Cid, w2c_cid_frame_s: &mut mpsc::UnboundedSender<(Cid, Frame)>, - end_receiver: oneshot::Receiver<()>, + end_r: oneshot::Receiver<()>, ) { trace!("Starting up udp read()"); let mut metrics_cache = CidFrameCache::new(self.metrics.frames_wire_in_total.clone(), cid); @@ -386,10 +333,10 @@ impl UdpProtocol { .wire_in_throughput .with_label_values(&[&cid.to_string()]); let mut data_in = self.data_in.lock().await; - let mut end_receiver = end_receiver.fuse(); + let mut end_r = end_r.fuse(); while let Some(bytes) = select! { r = data_in.next().fuse() => r, - _ = end_receiver => None, + _ = end_r => None, } { trace!("Got raw UDP message with len: {}", bytes.len()); let frame_no = bytes[0]; @@ -585,3 +532,108 @@ impl UdpProtocol { trace!("Shutting down udp write()"); } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + metrics::NetworkMetrics, + types::{Cid, Pid}, + }; + use async_std::net; + use futures::{executor::block_on, stream::StreamExt}; + use std::sync::Arc; + + #[test] + fn tcp_read_handshake() { + let pid = Pid::new(); + let cid = 80085; + let metrics = Arc::new(NetworkMetrics::new(&pid).unwrap()); + let addr = std::net::SocketAddrV4::new(std::net::Ipv4Addr::new(127, 0, 0, 1), 50500); + block_on(async { + let server = net::TcpListener::bind(addr).await.unwrap(); + let mut client = net::TcpStream::connect(addr).await.unwrap(); + + let s_stream = server.incoming().next().await.unwrap().unwrap(); + let prot = TcpProtocol::new(s_stream, metrics); + + //Send Handshake + client.write_all(&[FRAME_HANDSHAKE]).await.unwrap(); + client.write_all(b"HELLOWO").await.unwrap(); + client.write_all(&1337u32.to_le_bytes()).await.unwrap(); + client.write_all(&0u32.to_le_bytes()).await.unwrap(); + client.write_all(&42u32.to_le_bytes()).await.unwrap(); + client.flush(); + + //handle data + let (mut w2c_cid_frame_s, mut w2c_cid_frame_r) = mpsc::unbounded::<(Cid, Frame)>(); + let (read_stop_sender, read_stop_receiver) = oneshot::channel(); + let cid2 = cid; + let t = std::thread::spawn(move || { + block_on(async { + prot.read_from_wire(cid2, &mut w2c_cid_frame_s, read_stop_receiver) + .await; + }) + }); + // Assert than we get some value back! Its a Handshake! + //async_std::task::sleep(std::time::Duration::from_millis(1000)); + let (cid_r, frame) = w2c_cid_frame_r.next().await.unwrap(); + assert_eq!(cid, cid_r); + if let Frame::Handshake { + magic_number, + version, + } = frame + { + assert_eq!(&magic_number, b"HELLOWO"); + assert_eq!(version, [1337, 0, 42]); + } else { + panic!("wrong handshake"); + } + read_stop_sender.send(()).unwrap(); + t.join().unwrap(); + }); + } + + #[test] + fn tcp_read_garbage() { + let pid = Pid::new(); + let cid = 80085; + let metrics = Arc::new(NetworkMetrics::new(&pid).unwrap()); + let addr = std::net::SocketAddrV4::new(std::net::Ipv4Addr::new(127, 0, 0, 1), 50501); + block_on(async { + let server = net::TcpListener::bind(addr).await.unwrap(); + let mut client = net::TcpStream::connect(addr).await.unwrap(); + + let s_stream = server.incoming().next().await.unwrap().unwrap(); + let prot = TcpProtocol::new(s_stream, metrics); + + //Send Handshake + client + .write_all("x4hrtzsektfhxugzdtz5r78gzrtzfhxfdthfthuzhfzzufasgasdfg".as_bytes()) + .await + .unwrap(); + client.flush(); + + //handle data + let (mut w2c_cid_frame_s, mut w2c_cid_frame_r) = mpsc::unbounded::<(Cid, Frame)>(); + let (read_stop_sender, read_stop_receiver) = oneshot::channel(); + let cid2 = cid; + let t = std::thread::spawn(move || { + block_on(async { + prot.read_from_wire(cid2, &mut w2c_cid_frame_s, read_stop_receiver) + .await; + }) + }); + // Assert than we get some value back! Its a Raw! + let (cid_r, frame) = w2c_cid_frame_r.next().await.unwrap(); + assert_eq!(cid, cid_r); + if let Frame::Raw(data) = frame { + assert_eq!(&data.as_slice(), b"x4hrtzsektfhxugzdtz5r78gzrtzfhxf"); + } else { + panic!("wrong frame type"); + } + read_stop_sender.send(()).unwrap(); + t.join().unwrap(); + }); + } +} diff --git a/network/src/scheduler.rs b/network/src/scheduler.rs index 6679067048..bd83e3f924 100644 --- a/network/src/scheduler.rs +++ b/network/src/scheduler.rs @@ -368,8 +368,21 @@ impl Scheduler { next = incoming.next().fuse() => next, _ = end_receiver => None, } { - let stream = stream.unwrap(); - info!("Accepting Tcp from: {}", stream.peer_addr().unwrap()); + let stream = match stream { + Ok(s) => s, + Err(e) => { + warn!(?e, "TcpStream Error, ignoring connection attempt"); + continue; + }, + }; + let peer_addr = match stream.peer_addr() { + Ok(s) => s, + Err(e) => { + warn!(?e, "TcpStream Error, ignoring connection attempt"); + continue; + }, + }; + info!("Accepting Tcp from: {}", peer_addr); let protocol = TcpProtocol::new(stream, self.metrics.clone()); self.init_protocol(Protocols::Tcp(protocol), None, true) .await; @@ -583,6 +596,7 @@ impl Scheduler { Err(()) => { if let Some(pid_oneshot) = s2a_return_pid_s { // someone is waiting with `connect`, so give them their Error + trace!("returning the Err to api who requested the connect"); pid_oneshot .send(Err(std::io::Error::new( std::io::ErrorKind::PermissionDenied, diff --git a/server/src/lib.rs b/server/src/lib.rs index 9e104e3483..d7fab8912c 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -343,9 +343,9 @@ impl Server { // 3) Handle inputs from clients block_on(async { - //TIMEOUT 0.01 ms for msg handling + //TIMEOUT 0.1 ms for msg handling select!( - _ = Delay::new(std::time::Duration::from_micros(10)).fuse() => Ok(()), + _ = Delay::new(std::time::Duration::from_micros(100)).fuse() => Ok(()), err = self.handle_new_connections(&mut frontend_events).fuse() => err, ) })?; @@ -597,6 +597,7 @@ impl Server { ) -> Result<(), Error> { loop { let participant = self.network.connected().await?; + debug!("New Participant connected to the server"); let singleton_stream = participant.opened().await?; let mut client = Client { @@ -635,9 +636,9 @@ impl Server { time_of_day: *self.state.ecs().read_resource(), world_map: (WORLD_SIZE.map(|e| e as u32), self.map.clone()), }); - debug!("Done initial sync with client."); frontend_events.push(Event::ClientConnected { entity }); + debug!("Done initial sync with client."); } } } diff --git a/server/src/sys/message.rs b/server/src/sys/message.rs index 3ea09a0003..de7b10c233 100644 --- a/server/src/sys/message.rs +++ b/server/src/sys/message.rs @@ -473,9 +473,9 @@ impl<'a> System<'a> for Sys { let mut cnt = 0; let network_err: Result<(), crate::error::Error> = block_on(async { - //TIMEOUT 0.01 ms for msg handling + //TIMEOUT 0.02 ms for msg handling select!( - _ = Delay::new(std::time::Duration::from_micros(10)).fuse() => Ok(()), + _ = Delay::new(std::time::Duration::from_micros(20)).fuse() => Ok(()), err = Self::handle_client_msg( &mut server_emitter, &mut new_chat_msgs, From 187ec42aa2102f2e7d859c5829ec73077f0b2751 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=A4rtens?= Date: Sat, 11 Jul 2020 14:34:01 +0200 Subject: [PATCH 24/85] fix Participant shutdown - we had the problem that Participants couldn't shutdown them self, only by scheduler, which was controlled by api. it's needed e.g. to handle the Schudown Frame - my initial solution did a full shutdown, which was a problem if in parallel a 2nd shutdown was requested, no possibility of getting the error - new solution will only deactivate Participant and Stream. and then still functions correctly, till the api closes the participant and calls the scheduler which then calls the bparticipant again - i experimented with a Mutex or 2 and a `select` but it didn't prove that well - also adjusted the Error messages to now either Disconnected when gracefully shutdown or ProtocolFailed when some msg couldn't be delivered (note later might not be 100% returned correctly yet) --- network/src/api.rs | 104 +++++++++++----------------- network/src/participant.rs | 138 ++++++++++++++++++------------------- network/src/scheduler.rs | 2 + network/tests/closing.rs | 5 +- 4 files changed, 114 insertions(+), 135 deletions(-) diff --git a/network/src/api.rs b/network/src/api.rs index 71ebc3c86b..31dcae775c 100644 --- a/network/src/api.rs +++ b/network/src/api.rs @@ -53,7 +53,7 @@ pub struct Participant { remote_pid: Pid, a2b_steam_open_s: RwLock)>>, b2a_stream_opened_r: RwLock>, - closed: AtomicBool, + closed: Arc>>, a2s_disconnect_s: Arc>>, } @@ -92,11 +92,15 @@ pub enum NetworkError { } /// Error type thrown by [`Participants`](Participant) methods -#[derive(Debug)] +#[derive(Debug, PartialEq, Clone)] pub enum ParticipantError { - ///Participant was closed too early to complete the action fully - ParticipantClosed, - GracefulDisconnectFailed(std::io::Error), + ///Participant was closed by remote side + ParticipantDisconnected, + ///Underlying Protocol failed and wasn't able to recover, expect some Data + /// loss unfortunately, there is no method to get the exact messages + /// that failed. This is also returned when local side tries to do + /// something while remote site gracefully disconnects + ProtocolFailedUnrecoverable, } /// Error type thrown by [`Streams`](Stream) methods @@ -389,13 +393,14 @@ impl Participant { a2b_steam_open_s: mpsc::UnboundedSender<(Prio, Promises, oneshot::Sender)>, b2a_stream_opened_r: mpsc::UnboundedReceiver, a2s_disconnect_s: mpsc::UnboundedSender<(Pid, oneshot::Sender>)>, + closed: Arc>>, ) -> Self { Self { local_pid, remote_pid, a2b_steam_open_s: RwLock::new(a2b_steam_open_s), b2a_stream_opened_r: RwLock::new(b2a_stream_opened_r), - closed: AtomicBool::new(false), + closed, a2s_disconnect_s: Arc::new(Mutex::new(Some(a2s_disconnect_s))), } } @@ -444,20 +449,12 @@ impl Participant { //use this lock for now to make sure that only one open at a time is made, // TODO: not sure if we can paralise that, check in future let mut a2b_steam_open_s = self.a2b_steam_open_s.write().await; - if self.closed.load(Ordering::Relaxed) { - warn!(?self.remote_pid, "participant is closed but another open is tried on it"); - return Err(ParticipantError::ParticipantClosed); - } + self.closed.read().await.clone()?; let (p2a_return_stream_s, p2a_return_stream_r) = oneshot::channel(); - if a2b_steam_open_s + a2b_steam_open_s .send((prio, promises, p2a_return_stream_s)) .await - .is_err() - { - debug!(?self.remote_pid, "stream_open_sender failed, closing participant"); - self.closed.store(true, Ordering::Relaxed); - return Err(ParticipantError::ParticipantClosed); - } + .unwrap(); match p2a_return_stream_r.await { Ok(stream) => { let sid = stream.sid; @@ -466,8 +463,8 @@ impl Participant { }, Err(_) => { debug!(?self.remote_pid, "p2a_return_stream_r failed, closing participant"); - self.closed.store(true, Ordering::Relaxed); - Err(ParticipantError::ParticipantClosed) + *self.closed.write().await = Err(ParticipantError::ProtocolFailedUnrecoverable); + Err(ParticipantError::ProtocolFailedUnrecoverable) }, } } @@ -509,10 +506,7 @@ impl Participant { //use this lock for now to make sure that only one open at a time is made, // TODO: not sure if we can paralise that, check in future let mut stream_opened_receiver = self.b2a_stream_opened_r.write().await; - if self.closed.load(Ordering::Relaxed) { - warn!(?self.remote_pid, "Participant is closed but another open is tried on it"); - return Err(ParticipantError::ParticipantClosed); - } + self.closed.read().await.clone()?; match stream_opened_receiver.next().await { Some(stream) => { let sid = stream.sid; @@ -521,8 +515,8 @@ impl Participant { }, None => { debug!(?self.remote_pid, "stream_opened_receiver failed, closing participant"); - self.closed.store(true, Ordering::Relaxed); - Err(ParticipantError::ParticipantClosed) + *self.closed.write().await = Err(ParticipantError::ProtocolFailedUnrecoverable); + Err(ParticipantError::ProtocolFailedUnrecoverable) }, } } @@ -557,7 +551,7 @@ impl Participant { /// network /// .listen(ProtocolAddr::Tcp("0.0.0.0:2030".parse().unwrap())) /// .await?; - /// # remote.connect(ProtocolAddr::Tcp("0.0.0.0:2030".parse().unwrap())).await?; + /// # let keep_alive = remote.connect(ProtocolAddr::Tcp("0.0.0.0:2030".parse().unwrap())).await?; /// while let Ok(participant) = network.connected().await { /// println!("Participant connected: {}", participant.remote_pid()); /// participant.disconnect().await?; @@ -574,9 +568,13 @@ impl Participant { // Remove, Close and try_unwrap error when unwrap fails! let pid = self.remote_pid; debug!(?pid, "Closing participant from network"); - self.closed.store(true, Ordering::Relaxed); - //Streams will be closed by BParticipant + { + let mut lock = self.closed.write().await; + lock.clone()?; + *lock = Err(ParticipantError::ParticipantDisconnected); + } + //Streams will be closed by BParticipant match self.a2s_disconnect_s.lock().await.take() { Some(mut a2s_disconnect_s) => { let (finished_sender, finished_receiver) = oneshot::channel(); @@ -597,7 +595,7 @@ impl Participant { "Error occured during shutdown of participant and is propagated to \ User" ); - Err(ParticipantError::GracefulDisconnectFailed(e)) + Err(ParticipantError::ProtocolFailedUnrecoverable) }, Err(e) => { //this is a bug. but as i am Participant i can't destroy the network @@ -607,7 +605,7 @@ impl Participant { "Failed to get a message back from the scheduler, seems like the \ network is already closed" ); - Err(ParticipantError::ParticipantClosed) + Err(ParticipantError::ProtocolFailedUnrecoverable) }, } }, @@ -616,7 +614,7 @@ impl Participant { "seems like you are trying to disconnecting a participant after the network \ was already dropped. It was already dropped with the network!" ); - Err(ParticipantError::ParticipantClosed) + Err(ParticipantError::ParticipantDisconnected) }, } } @@ -907,19 +905,16 @@ impl Drop for Participant { .send((self.remote_pid, finished_sender)) .await .expect("Something is wrong in internal scheduler coding"); - match finished_receiver.await { - Ok(Err(e)) => error!( + match finished_receiver + .await + .expect("Something is wrong in internal scheduler/participant coding") + { + Err(e) => error!( ?pid, ?e, "Error while dropping the participant, couldn't send all outgoing \ messages, dropping remaining" ), - Err(e) => warn!( - ?e, - "//TODO i dont know why the finish doesnt work, i normally would \ - expect to have sended a return message from the participant... \ - ignoring to not caue a panic for now, please fix me" - ), _ => (), }; }); @@ -953,15 +948,10 @@ impl Drop for Stream { impl std::fmt::Debug for Participant { #[inline] fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let status = if self.closed.load(Ordering::Relaxed) { - "[CLOSED]" - } else { - "[OPEN]" - }; write!( f, - "Participant {{{} local_pid: {:?}, remote_pid: {:?} }}", - status, &self.local_pid, &self.remote_pid, + "Participant {{ local_pid: {:?}, remote_pid: {:?} }}", + &self.local_pid, &self.remote_pid, ) } } @@ -970,10 +960,6 @@ impl From> for StreamError { fn from(_err: crossbeam_channel::SendError) -> Self { StreamError::StreamClosed } } -impl From> for ParticipantError { - fn from(_err: crossbeam_channel::SendError) -> Self { ParticipantError::ParticipantClosed } -} - impl From> for NetworkError { fn from(_err: crossbeam_channel::SendError) -> Self { NetworkError::NetworkClosed } } @@ -982,26 +968,14 @@ impl From for StreamError { fn from(_err: std::option::NoneError) -> Self { StreamError::StreamClosed } } -impl From for ParticipantError { - fn from(_err: std::option::NoneError) -> Self { ParticipantError::ParticipantClosed } -} - impl From for NetworkError { fn from(_err: std::option::NoneError) -> Self { NetworkError::NetworkClosed } } -impl From for ParticipantError { - fn from(_err: mpsc::SendError) -> Self { ParticipantError::ParticipantClosed } -} - impl From for NetworkError { fn from(_err: mpsc::SendError) -> Self { NetworkError::NetworkClosed } } -impl From for ParticipantError { - fn from(_err: oneshot::Canceled) -> Self { ParticipantError::ParticipantClosed } -} - impl From for NetworkError { fn from(_err: oneshot::Canceled) -> Self { NetworkError::NetworkClosed } } @@ -1024,9 +998,9 @@ impl core::fmt::Display for StreamError { impl core::fmt::Display for ParticipantError { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { - ParticipantError::ParticipantClosed => write!(f, "Participant closed"), - ParticipantError::GracefulDisconnectFailed(_) => { - write!(f, "Graceful disconnect failed") + ParticipantError::ParticipantDisconnected => write!(f, "Participant disconnect"), + ParticipantError::ProtocolFailedUnrecoverable => { + write!(f, "underlying protocol failed unrecoverable") }, } } diff --git a/network/src/participant.rs b/network/src/participant.rs index fd6c32d798..08fb58cc7e 100644 --- a/network/src/participant.rs +++ b/network/src/participant.rs @@ -1,5 +1,5 @@ use crate::{ - api::Stream, + api::{ParticipantError, Stream}, channel::Channel, message::{IncomingMessage, MessageBuffer, OutgoingMessage}, metrics::{NetworkMetrics, PidCidFrameCache}, @@ -11,7 +11,6 @@ use async_std::sync::RwLock; use futures::{ channel::{mpsc, oneshot}, future::FutureExt, - lock::Mutex, select, sink::SinkExt, stream::StreamExt, @@ -54,12 +53,6 @@ struct ControlChannels { s2b_shutdown_bparticipant_r: oneshot::Receiver>>, /* own */ } -//this is needed in case of a shutdown -struct BParticipantShutdown { - b2b_prios_flushed_r: oneshot::Receiver<()>, - mgr_to_shutdown: Vec>, -} - #[derive(Debug)] pub struct BParticipant { remote_pid: Pid, @@ -67,10 +60,10 @@ pub struct BParticipant { offset_sid: Sid, channels: Arc>>, streams: RwLock>, + api_participant_closed: Arc>>, running_mgr: AtomicUsize, run_channels: Option, metrics: Arc, - shutdown_info: Mutex>, no_channel_error_info: RwLock<(Instant, u64)>, } @@ -86,6 +79,7 @@ impl BParticipant { mpsc::UnboundedReceiver, mpsc::UnboundedSender<(Cid, Sid, Protocols, Vec<(Cid, Frame)>, oneshot::Sender<()>)>, oneshot::Sender>>, + Arc>>, ) { let (a2b_steam_open_s, a2b_steam_open_r) = mpsc::unbounded::<(Prio, Promises, oneshot::Sender)>(); @@ -103,6 +97,8 @@ impl BParticipant { s2b_shutdown_bparticipant_r, }); + let api_participant_closed = Arc::new(RwLock::new(Ok(()))); + ( Self { remote_pid, @@ -110,16 +106,17 @@ impl BParticipant { offset_sid, channels: Arc::new(RwLock::new(vec![])), streams: RwLock::new(HashMap::new()), + api_participant_closed: api_participant_closed.clone(), running_mgr: AtomicUsize::new(0), run_channels, metrics, no_channel_error_info: RwLock::new((Instant::now(), 0)), - shutdown_info: Mutex::new(None), }, a2b_steam_open_s, b2a_stream_opened_r, s2b_create_channel_s, s2b_shutdown_bparticipant_s, + api_participant_closed, ) } @@ -134,14 +131,6 @@ impl BParticipant { let (w2b_frames_s, w2b_frames_r) = mpsc::unbounded::<(Cid, Frame)>(); let (prios, a2p_msg_s, b2p_notify_empty_stream_s) = PrioManager::new(self.metrics.clone(), self.remote_pid_string.clone()); - *self.shutdown_info.lock().await = Some(BParticipantShutdown { - b2b_prios_flushed_r, - mgr_to_shutdown: vec![ - shutdown_send_mgr_sender, - shutdown_open_mgr_sender, - shutdown_stream_close_mgr_sender, - ], - }); let run_channels = self.run_channels.take().unwrap(); futures::join!( @@ -169,7 +158,15 @@ impl BParticipant { shutdown_stream_close_mgr_receiver, b2p_notify_empty_stream_s, ), - self.participant_shutdown_mgr(run_channels.s2b_shutdown_bparticipant_r,), + self.participant_shutdown_mgr( + run_channels.s2b_shutdown_bparticipant_r, + b2b_prios_flushed_r, + vec![ + shutdown_send_mgr_sender, + shutdown_open_mgr_sender, + shutdown_stream_close_mgr_sender, + ], + ), ); } @@ -190,7 +187,6 @@ impl BParticipant { trace!("Start send_mgr"); let mut send_cache = PidCidFrameCache::new(self.metrics.frames_out_total.clone(), self.remote_pid); - //while !self.closed.load(Ordering::Relaxed) { loop { let mut frames = VecDeque::new(); prios.fill_frames(FRAMES_PER_TICK, &mut frames).await; @@ -258,7 +254,8 @@ impl BParticipant { will be closed, but not if we do channel-takeover" ); //TEMP FIX: as we dont have channel takeover yet drop the whole bParticipant - self.close_participant(2).await; + self.close_api(ParticipantError::ProtocolFailedUnrecoverable) + .await; false } else { true @@ -393,7 +390,8 @@ impl BParticipant { }, Frame::Shutdown => { debug!("Shutdown received from remote side"); - self.close_participant(2).await; + self.close_api(ParticipantError::ParticipantDisconnected) + .await; }, f => unreachable!("Frame should never reache participant!: {:?}", f), } @@ -510,14 +508,59 @@ impl BParticipant { /// wait for everything to go right! Then return 1. Shutting down /// Streams for API and End user! 2. Wait for all "prio queued" Messages /// to be send. 3. Send Stream + /// If BParticipant kills itself managers stay active till this function is + /// called by api to get the result status async fn participant_shutdown_mgr( &self, s2b_shutdown_bparticipant_r: oneshot::Receiver>>, + b2b_prios_flushed_r: oneshot::Receiver<()>, + mut mgr_to_shutdown: Vec>, ) { self.running_mgr.fetch_add(1, Ordering::Relaxed); trace!("Start participant_shutdown_mgr"); let sender = s2b_shutdown_bparticipant_r.await.unwrap(); - self.close_participant(1).await; + + //Todo: isn't ParticipantDisconnected useless, as api is waiting rn for a + // callback? + self.close_api(ParticipantError::ParticipantDisconnected) + .await; + + debug!("Closing all managers"); + for sender in mgr_to_shutdown.drain(..) { + if let Err(e) = sender.send(()) { + warn!(?e, "Manager seems to be closed already, weird, maybe a bug"); + }; + } + + b2b_prios_flushed_r.await.unwrap(); + debug!("Closing all channels, after flushed prios"); + for ci in self.channels.write().await.drain(..) { + if let Err(e) = ci.b2r_read_shutdown.send(()) { + debug!(?e, ?ci.cid, "Seems like this read protocol got already dropped by closing the Stream itself, just ignoring the fact"); + }; + } + + //Wait for other bparticipants mgr to close via AtomicUsize + const SLEEP_TIME: Duration = Duration::from_millis(5); + const ALLOWED_MANAGER: usize = 1; + async_std::task::sleep(SLEEP_TIME).await; + let mut i: u32 = 1; + while self.running_mgr.load(Ordering::Relaxed) > ALLOWED_MANAGER { + i += 1; + if i.rem_euclid(10) == 1 { + trace!( + ?ALLOWED_MANAGER, + "Waiting for bparticipant mgr to shut down, remaining {}", + self.running_mgr.load(Ordering::Relaxed) - ALLOWED_MANAGER + ); + } + async_std::task::sleep(SLEEP_TIME * i).await; + } + trace!("All BParticipant mgr (except me) are shut down now"); + + self.metrics.participants_disconnected_total.inc(); + debug!("BParticipant close done"); + sender.send(Ok(())).unwrap(); trace!("Stop participant_shutdown_mgr"); self.running_mgr.fetch_sub(1, Ordering::Relaxed); @@ -613,56 +656,13 @@ impl BParticipant { ) } - /// this will gracefully shut down the bparticipant - /// allowed_managers: the number of open managers to sleep on. Must be 1 for - /// shutdown_mgr and 2 if it comes from a send error. - async fn close_participant(&self, allowed_managers: usize) { - trace!("Participant shutdown triggered"); - let mut info = match self.shutdown_info.lock().await.take() { - Some(info) => info, - None => { - //This can happen if >=2 different async fn found out the protocol got dropped - // but they haven't shut down so far - debug!("Close of participant seemed to be called twice, ignoring the 2nd close"); - return; - }, - }; - debug!("Closing all managers"); - for sender in info.mgr_to_shutdown.drain(..) { - if let Err(e) = sender.send(()) { - warn!(?e, "Manager seems to be closed already, weird, maybe a bug"); - }; - } + /// close streams and set err + async fn close_api(&self, err: ParticipantError) { + *self.api_participant_closed.write().await = Err(err); debug!("Closing all streams"); for (sid, si) in self.streams.write().await.drain() { trace!(?sid, "Shutting down Stream"); si.closed.store(true, Ordering::Relaxed); } - debug!("Waiting for prios to be flushed"); - info.b2b_prios_flushed_r.await.unwrap(); - debug!("Closing all channels"); - for ci in self.channels.write().await.drain(..) { - if let Err(e) = ci.b2r_read_shutdown.send(()) { - debug!(?e, ?ci.cid, "Seems like this read protocol got already dropped by closing the Stream itself, just ignoring the fact"); - }; - } - //Wait for other bparticipants mgr to close via AtomicUsize - const SLEEP_TIME: Duration = Duration::from_millis(5); - async_std::task::sleep(SLEEP_TIME).await; - let mut i: u32 = 1; - while self.running_mgr.load(Ordering::Relaxed) > allowed_managers { - i += 1; - if i.rem_euclid(10) == 1 { - trace!( - ?allowed_managers, - "Waiting for bparticipant mgr to shut down, remaining {}", - self.running_mgr.load(Ordering::Relaxed) - allowed_managers - ); - } - async_std::task::sleep(SLEEP_TIME * i).await; - } - trace!("All BParticipant mgr (except me) are shut down now"); - self.metrics.participants_disconnected_total.inc(); - debug!("BParticipant close done"); } } diff --git a/network/src/scheduler.rs b/network/src/scheduler.rs index bd83e3f924..7917266638 100644 --- a/network/src/scheduler.rs +++ b/network/src/scheduler.rs @@ -516,6 +516,7 @@ impl Scheduler { b2a_stream_opened_r, mut s2b_create_channel_s, s2b_shutdown_bparticipant_s, + api_participant_closed, ) = BParticipant::new(pid, sid, metrics.clone()); let participant = Participant::new( @@ -524,6 +525,7 @@ impl Scheduler { a2b_steam_open_s, b2a_stream_opened_r, participant_channels.a2s_disconnect_s, + api_participant_closed, ); metrics.participants_connected_total.inc(); diff --git a/network/tests/closing.rs b/network/tests/closing.rs index 2bd9a92acd..1b5b74fb2a 100644 --- a/network/tests/closing.rs +++ b/network/tests/closing.rs @@ -22,7 +22,10 @@ fn close_participant() { let (_n_a, p1_a, mut s1_a, _n_b, p1_b, mut s1_b) = block_on(network_participant_stream(tcp())); block_on(p1_a.disconnect()).unwrap(); - block_on(p1_b.disconnect()).unwrap(); + //We dont know of if the disconect is done YET, so the next command will either + // return already closed or fail a gracefully close as it will discover that the + // remote site closed right now + assert!(block_on(p1_b.disconnect()).is_err()); assert_eq!(s1_a.send("Hello World"), Err(StreamError::StreamClosed)); assert_eq!( From 6db9c6f91b8ea2e06e2661a2e0b0a30899f83f5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=A4rtens?= Date: Sat, 11 Jul 2020 16:08:25 +0200 Subject: [PATCH 25/85] fix a followup bug, after a protocol fail now Participant is closed, including all streams, so we get the stream errors. We MUST handle them and we are not allowed to act on a stream after it failed, as i am to lazy to change the structure to ensure the client to be imeadiatly dropped i added a AtomicBool to it. --- client/src/lib.rs | 3 +-- network/src/api.rs | 7 +++---- network/src/message.rs | 2 +- network/tests/closing.rs | 30 +++++++++++++++++++++++++++--- server/src/client.rs | 38 ++++++++++++++++++++++++++++++++------ server/src/lib.rs | 1 + server/src/sys/message.rs | 6 +++--- 7 files changed, 68 insertions(+), 19 deletions(-) diff --git a/client/src/lib.rs b/client/src/lib.rs index accc589818..9e939c91c8 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -613,8 +613,7 @@ impl Client { ); } self.singleton_stream - .send(ClientMsg::ControllerInputs(inputs)) - .unwrap(); + .send(ClientMsg::ControllerInputs(inputs))?; } // 2) Build up a list of events for this frame, to be passed to the frontend. diff --git a/network/src/api.rs b/network/src/api.rs index 31dcae775c..d31de577b2 100644 --- a/network/src/api.rs +++ b/network/src/api.rs @@ -905,17 +905,16 @@ impl Drop for Participant { .send((self.remote_pid, finished_sender)) .await .expect("Something is wrong in internal scheduler coding"); - match finished_receiver + if let Err(e) = finished_receiver .await .expect("Something is wrong in internal scheduler/participant coding") { - Err(e) => error!( + error!( ?pid, ?e, "Error while dropping the participant, couldn't send all outgoing \ messages, dropping remaining" - ), - _ => (), + ); }; }); }, diff --git a/network/src/message.rs b/network/src/message.rs index baf6ef6ce1..03626f22d3 100644 --- a/network/src/message.rs +++ b/network/src/message.rs @@ -45,7 +45,7 @@ pub(crate) fn serialize(message: &M) -> MessageBuffer { // std::Result> { pub(crate) fn deserialize(buffer: MessageBuffer) -> bincode::Result { let span = lz4_compress::decompress(&buffer.data) - .expect("lz4_compression error, failed to deserialze"); + .expect("lz4 decompression failed, failed to deserialze"); //this might fail if you choose the wrong type for M. in that case probably X // got transfered while you assume Y. probably this means your application // logic is wrong. E.g. You expect a String, but just get a u8. diff --git a/network/tests/closing.rs b/network/tests/closing.rs index 1b5b74fb2a..9b79f9c88a 100644 --- a/network/tests/closing.rs +++ b/network/tests/closing.rs @@ -1,3 +1,23 @@ +//! How to read those tests: +//! - in the first line we call the helper, this is only debug code. in case +//! you want to have tracing for a special test you set set the bool = true +//! and the sleep to 10000 and your test will start 10 sec delayed with +//! tracing. You need a delay as otherwise the other tests polute your trace +//! - the second line is to simulate a client and a server +//! `network_participant_stream` will return +//! - 2 networks +//! - 2 participants +//! - 2 streams +//! each one `linked` to their counterpart. +//! You see a cryptic use of rust `_` this is because we are testing the +//! `drop` behavior here. +//! - A `_` means this is directly dropped after the line executes, thus +//! immediately executing its `Drop` impl. +//! - A `_p1_a` e.g. means we don't use that Participant yet, but we must +//! not `drop` it yet as we might want to use the Streams. +//! - You sometimes see sleep(1000ms) this is used when we rely on the +//! underlying TCP functionality, as this simulates client and server + use async_std::task; use task::block_on; use veloren_network::StreamError; @@ -22,9 +42,13 @@ fn close_participant() { let (_n_a, p1_a, mut s1_a, _n_b, p1_b, mut s1_b) = block_on(network_participant_stream(tcp())); block_on(p1_a.disconnect()).unwrap(); - //We dont know of if the disconect is done YET, so the next command will either - // return already closed or fail a gracefully close as it will discover that the - // remote site closed right now + // The following will `Err`, but we don't know the exact error message. + // Why? because of the TCP layer we have no guarantee if the TCP messages send + // one line above already reached `p1_b`. If they reached them it would fail + // with a `ParticipantDisconnected` as a clean disconnect was performed. + // If they haven't reached them yet but will reach them during the execution it + // will return a unclean shutdown was detected. Nevertheless, if it returns + // Ok(()) then something is wrong! assert!(block_on(p1_b.disconnect()).is_err()); assert_eq!(s1_a.send("Hello World"), Err(StreamError::StreamClosed)); diff --git a/server/src/client.rs b/server/src/client.rs index 345d674381..95e6d96b91 100644 --- a/server/src/client.rs +++ b/server/src/client.rs @@ -1,15 +1,21 @@ -use common::msg::{ClientState, RequestStateError, ServerMsg}; +use crate::error::Error; +use common::msg::{ClientMsg, ClientState, RequestStateError, ServerMsg}; use hashbrown::HashSet; use network::{Participant, Stream}; use specs::{Component, FlaggedStorage}; use specs_idvs::IdvStorage; -use std::sync::Mutex; +use std::sync::{ + atomic::{AtomicBool, Ordering}, + Mutex, +}; +use tracing::debug; use vek::*; pub struct Client { pub client_state: ClientState, pub participant: Mutex>, pub singleton_stream: Stream, + pub network_error: AtomicBool, pub last_ping: f64, pub login_msg_sent: bool, } @@ -19,7 +25,29 @@ impl Component for Client { } impl Client { - pub fn notify(&mut self, msg: ServerMsg) { let _ = self.singleton_stream.send(msg); } + pub fn notify(&mut self, msg: ServerMsg) { + if !self.network_error.load(Ordering::Relaxed) { + if let Err(e) = self.singleton_stream.send(msg) { + debug!(?e, "got a network error with client"); + self.network_error.store(true, Ordering::Relaxed); + } + } + } + + pub async fn recv(&mut self) -> Result { + if !self.network_error.load(Ordering::Relaxed) { + match self.singleton_stream.recv().await { + Ok(r) => Ok(r), + Err(e) => { + debug!(?e, "got a network error with client while recv"); + self.network_error.store(true, Ordering::Relaxed); + Err(Error::StreamErr(e)) + }, + } + } else { + Err(Error::StreamErr(network::StreamError::StreamClosed)) + } + } pub fn is_registered(&self) -> bool { match self.client_state { @@ -43,9 +71,7 @@ impl Client { } pub fn error_state(&mut self, error: RequestStateError) { - let _ = self - .singleton_stream - .send(ServerMsg::StateAnswer(Err((error, self.client_state)))); + let _ = self.notify(ServerMsg::StateAnswer(Err((error, self.client_state)))); } } diff --git a/server/src/lib.rs b/server/src/lib.rs index d7fab8912c..11d7d95093 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -604,6 +604,7 @@ impl Server { client_state: ClientState::Connected, participant: std::sync::Mutex::new(Some(participant)), singleton_stream, + network_error: std::sync::atomic::AtomicBool::new(false), last_ping: self.state.get_time(), login_msg_sent: false, }; diff --git a/server/src/sys/message.rs b/server/src/sys/message.rs index de7b10c233..a6b6eb5f0c 100644 --- a/server/src/sys/message.rs +++ b/server/src/sys/message.rs @@ -27,8 +27,8 @@ use specs::{ }; impl Sys { - ///We need to move this to a async fn, otherwise the compiler generates to - /// much recursive fn, and async closures dont work yet + ///We needed to move this to a async fn, if we would use a async closures + /// the compiler generates to much recursion and fails to compile this #[allow(clippy::too_many_arguments)] async fn handle_client_msg( server_emitter: &mut common::event::Emitter<'_, ServerEvent>, @@ -57,7 +57,7 @@ impl Sys { settings: &Read<'_, ServerSettings>, ) -> Result<(), crate::error::Error> { loop { - let msg = client.singleton_stream.recv().await?; + let msg = client.recv().await?; *cnt += 1; match msg { // Go back to registered state (char selection screen) From a9be045cb8c281f68d3b715a642ec0d7b2edd086 Mon Sep 17 00:00:00 2001 From: Ben Wallis Date: Mon, 13 Jul 2020 20:57:13 +0100 Subject: [PATCH 26/85] Fixed suppressed clippy warnings for #587 - useless_conversion --- server/src/metrics.rs | 5 +- voxygen/src/anim/character/glidewield.rs | 2 +- voxygen/src/anim/src/character/charge.rs | 6 +- voxygen/src/anim/src/character/glidewield.rs | 6 +- voxygen/src/anim/src/character/gliding.rs | 6 +- voxygen/src/anim/src/character/jump.rs | 6 +- voxygen/src/anim/src/character/roll.rs | 6 +- voxygen/src/anim/src/character/run.rs | 6 +- voxygen/src/anim/src/character/swim.rs | 6 +- voxygen/src/anim/src/quadruped_low/run.rs | 5 +- voxygen/src/anim/src/quadruped_medium/run.rs | 5 +- voxygen/src/render/error.rs | 3 +- world/src/civ/mod.rs | 11 +- world/src/lib.rs | 3 +- world/src/sim/map.rs | 351 +++++++++---------- 15 files changed, 207 insertions(+), 220 deletions(-) diff --git a/server/src/metrics.rs b/server/src/metrics.rs index 3fcf94cf4f..11913abd7a 100644 --- a/server/src/metrics.rs +++ b/server/src/metrics.rs @@ -33,7 +33,6 @@ pub struct ServerMetrics { } impl TickMetrics { - #[allow(clippy::useless_conversion)] // TODO: Pending review in #587 pub fn new(registry: &Registry, tick: Arc) -> Result> { let player_online = IntGauge::with_opts(Opts::new( "player_online", @@ -65,10 +64,10 @@ impl TickMetrics { "chunks_count", "number of all chunks currently active on the server", ))?; - let tick_time = IntGaugeVec::from(IntGaugeVec::new( + let tick_time = IntGaugeVec::new( Opts::new("tick_time", "time in ns requiered for a tick of the server"), &["period"], - )?); + )?; let since_the_epoch = SystemTime::now() .duration_since(UNIX_EPOCH) diff --git a/voxygen/src/anim/character/glidewield.rs b/voxygen/src/anim/character/glidewield.rs index 0b0985e64a..054dceda65 100644 --- a/voxygen/src/anim/character/glidewield.rs +++ b/voxygen/src/anim/character/glidewield.rs @@ -9,7 +9,7 @@ impl Animation for GlideWieldAnimation { type Dependency = (Option, Option, Vec3, Vec3, Vec3, f64); type Skeleton = CharacterSkeleton; - #[allow(clippy::useless_conversion)] // TODO: Pending review in #587 + fn update_skeleton( skeleton: &Self::Skeleton, (_active_tool_kind, _second_tool_kind, velocity, orientation, last_ori, global_time): Self::Dependency, diff --git a/voxygen/src/anim/src/character/charge.rs b/voxygen/src/anim/src/character/charge.rs index 0563243364..e2581c3e2b 100644 --- a/voxygen/src/anim/src/character/charge.rs +++ b/voxygen/src/anim/src/character/charge.rs @@ -21,7 +21,7 @@ impl Animation for ChargeAnimation { #[cfg_attr(feature = "be-dyn-lib", export_name = "character_charge")] #[allow(clippy::approx_constant)] // TODO: Pending review in #587 - #[allow(clippy::useless_conversion)] // TODO: Pending review in #587 + fn update_skeleton_inner( skeleton: &Self::Skeleton, (active_tool_kind, second_tool_kind, velocity, orientation, last_ori, _global_time): Self::Dependency, @@ -60,10 +60,10 @@ impl Animation for ChargeAnimation { let stop = ((anim_time as f32).powf(0.3 as f32)).min(1.2); let stopa = ((anim_time as f32).powf(0.9 as f32)).min(5.0); - let ori = Vec2::from(orientation); + let ori: Vec2 = Vec2::from(orientation); let last_ori = Vec2::from(last_ori); let tilt = if Vec2::new(ori, last_ori) - .map(|o| Vec2::::from(o).magnitude_squared()) + .map(|o| o.magnitude_squared()) .map(|m| m > 0.001 && m.is_finite()) .reduce_and() && ori.angle_between(last_ori).is_finite() diff --git a/voxygen/src/anim/src/character/glidewield.rs b/voxygen/src/anim/src/character/glidewield.rs index 70646f2f95..c60a985410 100644 --- a/voxygen/src/anim/src/character/glidewield.rs +++ b/voxygen/src/anim/src/character/glidewield.rs @@ -22,7 +22,7 @@ impl Animation for GlideWieldAnimation { const UPDATE_FN: &'static [u8] = b"character_glidewield\0"; #[cfg_attr(feature = "be-dyn-lib", export_name = "character_glidewield")] - #[allow(clippy::useless_conversion)] // TODO: Pending review in #587 + fn update_skeleton_inner( skeleton: &Self::Skeleton, (active_tool_kind, second_tool_kind, velocity, orientation, last_ori, global_time): Self::Dependency, @@ -94,10 +94,10 @@ impl Animation for GlideWieldAnimation { * 0.1, ); - let ori = Vec2::from(orientation); + let ori: Vec2 = Vec2::from(orientation); let last_ori = Vec2::from(last_ori); let tilt = if Vec2::new(ori, last_ori) - .map(|o| Vec2::::from(o).magnitude_squared()) + .map(|o| o.magnitude_squared()) .map(|m| m > 0.001 && m.is_finite()) .reduce_and() && ori.angle_between(last_ori).is_finite() diff --git a/voxygen/src/anim/src/character/gliding.rs b/voxygen/src/anim/src/character/gliding.rs index 704523b333..615d7d0a1d 100644 --- a/voxygen/src/anim/src/character/gliding.rs +++ b/voxygen/src/anim/src/character/gliding.rs @@ -22,7 +22,7 @@ impl Animation for GlidingAnimation { const UPDATE_FN: &'static [u8] = b"character_gliding\0"; #[cfg_attr(feature = "be-dyn-lib", export_name = "character_gliding")] - #[allow(clippy::useless_conversion)] // TODO: Pending review in #587 + fn update_skeleton_inner( skeleton: &Self::Skeleton, (active_tool_kind, second_tool_kind, velocity, orientation, last_ori, global_time): Self::Dependency, @@ -54,10 +54,10 @@ impl Animation for GlidingAnimation { * 0.25, ); - let ori = Vec2::from(orientation); + let ori: Vec2 = Vec2::from(orientation); let last_ori = Vec2::from(last_ori); let tilt = if Vec2::new(ori, last_ori) - .map(|o| Vec2::::from(o).magnitude_squared()) + .map(|o| o.magnitude_squared()) .map(|m| m > 0.0001 && m.is_finite()) .reduce_and() && ori.angle_between(last_ori).is_finite() diff --git a/voxygen/src/anim/src/character/jump.rs b/voxygen/src/anim/src/character/jump.rs index 33730731c1..62cccf8941 100644 --- a/voxygen/src/anim/src/character/jump.rs +++ b/voxygen/src/anim/src/character/jump.rs @@ -18,7 +18,7 @@ impl Animation for JumpAnimation { const UPDATE_FN: &'static [u8] = b"character_jump\0"; #[cfg_attr(feature = "be-dyn-lib", export_name = "character_jump")] - #[allow(clippy::useless_conversion)] // TODO: Pending review in #587 + fn update_skeleton_inner( skeleton: &Self::Skeleton, (active_tool_kind, second_tool_kind, orientation, last_ori, global_time): Self::Dependency, @@ -39,10 +39,10 @@ impl Animation for JumpAnimation { let switch = if random > 0.5 { 1.0 } else { -1.0 }; - let ori = Vec2::from(orientation); + let ori: Vec2 = Vec2::from(orientation); let last_ori = Vec2::from(last_ori); let tilt = if Vec2::new(ori, last_ori) - .map(|o| Vec2::::from(o).magnitude_squared()) + .map(|o| o.magnitude_squared()) .map(|m| m > 0.001 && m.is_finite()) .reduce_and() && ori.angle_between(last_ori).is_finite() diff --git a/voxygen/src/anim/src/character/roll.rs b/voxygen/src/anim/src/character/roll.rs index 7a31246219..5079d119c7 100644 --- a/voxygen/src/anim/src/character/roll.rs +++ b/voxygen/src/anim/src/character/roll.rs @@ -19,7 +19,7 @@ impl Animation for RollAnimation { const UPDATE_FN: &'static [u8] = b"character_roll\0"; #[cfg_attr(feature = "be-dyn-lib", export_name = "character_roll")] - #[allow(clippy::useless_conversion)] // TODO: Pending review in #587 + fn update_skeleton_inner( skeleton: &Self::Skeleton, (active_tool_kind, second_tool_kind, orientation, last_ori, _global_time): Self::Dependency, @@ -31,10 +31,10 @@ impl Animation for RollAnimation { let mut next = (*skeleton).clone(); let spin = anim_time as f32; - let ori = Vec2::from(orientation); + let ori: Vec2 = Vec2::from(orientation); let last_ori = Vec2::from(last_ori); let tilt = if Vec2::new(ori, last_ori) - .map(|o| Vec2::::from(o).magnitude_squared()) + .map(|o| o.magnitude_squared()) .map(|m| m > 0.0001 && m.is_finite()) .reduce_and() && ori.angle_between(last_ori).is_finite() diff --git a/voxygen/src/anim/src/character/run.rs b/voxygen/src/anim/src/character/run.rs index 35bd0cacdd..a2d24a2bd1 100644 --- a/voxygen/src/anim/src/character/run.rs +++ b/voxygen/src/anim/src/character/run.rs @@ -23,7 +23,7 @@ impl Animation for RunAnimation { const UPDATE_FN: &'static [u8] = b"character_run\0"; #[cfg_attr(feature = "be-dyn-lib", export_name = "character_run")] - #[allow(clippy::useless_conversion)] // TODO: Pending review in #587 + fn update_skeleton_inner( skeleton: &Self::Skeleton, (active_tool_kind, second_tool_kind, velocity, orientation, last_ori, global_time, avg_vel): Self::Dependency, @@ -96,10 +96,10 @@ impl Animation for RunAnimation { * 0.1, ); - let ori = Vec2::from(orientation); + let ori: Vec2 = Vec2::from(orientation); let last_ori = Vec2::from(last_ori); let tilt = if Vec2::new(ori, last_ori) - .map(|o| Vec2::::from(o).magnitude_squared()) + .map(|o| o.magnitude_squared()) .map(|m| m > 0.001 && m.is_finite()) .reduce_and() && ori.angle_between(last_ori).is_finite() diff --git a/voxygen/src/anim/src/character/swim.rs b/voxygen/src/anim/src/character/swim.rs index 12e9be25d3..cbabf7c081 100644 --- a/voxygen/src/anim/src/character/swim.rs +++ b/voxygen/src/anim/src/character/swim.rs @@ -22,7 +22,7 @@ impl Animation for SwimAnimation { const UPDATE_FN: &'static [u8] = b"character_swim\0"; #[cfg_attr(feature = "be-dyn-lib", export_name = "character_swim")] - #[allow(clippy::useless_conversion)] // TODO: Pending review in #587 + fn update_skeleton_inner( skeleton: &Self::Skeleton, (active_tool_kind, second_tool_kind, velocity, orientation, last_ori, global_time): Self::Dependency, @@ -57,10 +57,10 @@ impl Animation for SwimAnimation { .sin() * 0.1, ); - let ori = Vec2::from(orientation); + let ori: Vec2 = Vec2::from(orientation); let last_ori = Vec2::from(last_ori); let tilt = if Vec2::new(ori, last_ori) - .map(|o| Vec2::::from(o).magnitude_squared()) + .map(|o| o.magnitude_squared()) .map(|m| m > 0.001 && m.is_finite()) .reduce_and() && ori.angle_between(last_ori).is_finite() diff --git a/voxygen/src/anim/src/quadruped_low/run.rs b/voxygen/src/anim/src/quadruped_low/run.rs index 0a69bbfc30..259d660929 100644 --- a/voxygen/src/anim/src/quadruped_low/run.rs +++ b/voxygen/src/anim/src/quadruped_low/run.rs @@ -11,7 +11,6 @@ impl Animation for RunAnimation { #[cfg(feature = "use-dyn-lib")] const UPDATE_FN: &'static [u8] = b"quadruped_low_run\0"; - #[allow(clippy::useless_conversion)] // TODO: Pending review in #587 #[cfg_attr(feature = "be-dyn-lib", export_name = "quadruped_low_run")] fn update_skeleton_inner( skeleton: &Self::Skeleton, @@ -71,10 +70,10 @@ impl Animation for RunAnimation { * ((anim_time as f32 * 16.0 * lab as f32 + PI * 0.05).sin()); let footvertrb = (anim_time as f32 * 16.0 * lab as f32 + PI * 0.6).sin(); - let ori = Vec2::from(orientation); + let ori: Vec2 = Vec2::from(orientation); let last_ori = Vec2::from(last_ori); let tilt = if Vec2::new(ori, last_ori) - .map(|o| Vec2::::from(o).magnitude_squared()) + .map(|o| o.magnitude_squared()) .map(|m| m > 0.001 && m.is_finite()) .reduce_and() && ori.angle_between(last_ori).is_finite() diff --git a/voxygen/src/anim/src/quadruped_medium/run.rs b/voxygen/src/anim/src/quadruped_medium/run.rs index 5feef6b718..5dadddf37d 100644 --- a/voxygen/src/anim/src/quadruped_medium/run.rs +++ b/voxygen/src/anim/src/quadruped_medium/run.rs @@ -11,7 +11,6 @@ impl Animation for RunAnimation { #[cfg(feature = "use-dyn-lib")] const UPDATE_FN: &'static [u8] = b"quadruped_medium_run\0"; - #[allow(clippy::useless_conversion)] // TODO: Pending review in #587 #[cfg_attr(feature = "be-dyn-lib", export_name = "quadruped_medium_run")] fn update_skeleton_inner( skeleton: &Self::Skeleton, @@ -58,10 +57,10 @@ impl Animation for RunAnimation { let footvertaltfslow = (anim_time as f32 * 16.0 * lab as f32 * speedmult + PI * 1.8).sin(); let footverttaltfslow = (anim_time as f32 * 16.0 * lab as f32 * speedmult + PI * 2.2).sin(); // - let ori = Vec2::from(orientation); + let ori: Vec2 = Vec2::from(orientation); let last_ori = Vec2::from(last_ori); let tilt = if Vec2::new(ori, last_ori) - .map(|o| Vec2::::from(o).magnitude_squared()) + .map(|o| o.magnitude_squared()) .map(|m| m > 0.001 && m.is_finite()) .reduce_and() && ori.angle_between(last_ori).is_finite() diff --git a/voxygen/src/render/error.rs b/voxygen/src/render/error.rs index dcf74680d9..a0437c4794 100644 --- a/voxygen/src/render/error.rs +++ b/voxygen/src/render/error.rs @@ -17,11 +17,10 @@ impl From> for RenderError { } impl From> for RenderError { - #[allow(clippy::useless_conversion)] // TODO: Pending review in #587 fn from(err: gfx::PipelineStateError<&str>) -> Self { match err { gfx::PipelineStateError::DescriptorInit(err) => { - gfx::PipelineStateError::DescriptorInit(err.into()) + gfx::PipelineStateError::DescriptorInit(err) }, err => err, } diff --git a/world/src/civ/mod.rs b/world/src/civ/mod.rs index d75ca1f65a..326ec2b0ae 100644 --- a/world/src/civ/mod.rs +++ b/world/src/civ/mod.rs @@ -70,7 +70,6 @@ impl<'a, R: Rng> GenCtx<'a, R> { } impl Civs { - #[allow(clippy::useless_conversion)] // TODO: Pending review in #587 pub fn generate(seed: u32, sim: &mut WorldSim) -> Self { let mut this = Self::default(); let rng = ChaChaRng::from_seed(seed_expan::rng_state(seed)); @@ -120,7 +119,7 @@ impl Civs { for site in this.sites.iter() { let radius = 48i32; - let wpos = site.center * Vec2::from(TerrainChunkSize::RECT_SIZE).map(|e: u32| e as i32); + let wpos = site.center * TerrainChunkSize::RECT_SIZE.map(|e: u32| e as i32); let flatten_radius = match &site.kind { SiteKind::Settlement => 10.0, @@ -164,11 +163,9 @@ impl Civs { let mut cnt = 0; for site in this.sites.iter() { cnt += 1; - let wpos = site - .center - .map2(Vec2::from(TerrainChunkSize::RECT_SIZE), |e, sz: u32| { - e * sz as i32 + sz as i32 / 2 - }); + let wpos = site.center.map2(TerrainChunkSize::RECT_SIZE, |e, sz: u32| { + e * sz as i32 + sz as i32 / 2 + }); let mut rng = ctx.reseed().rng; let world_site = match &site.kind { diff --git a/world/src/lib.rs b/world/src/lib.rs index 38a5158fd2..12e6112947 100644 --- a/world/src/lib.rs +++ b/world/src/lib.rs @@ -64,7 +64,6 @@ impl World { pub fn sample_blocks(&self) -> BlockGen { BlockGen::new(ColumnGen::new(&self.sim)) } - #[allow(clippy::useless_conversion)] // TODO: Pending review in #587 #[allow(clippy::or_fun_call)] // TODO: Pending review in #587 pub fn generate_chunk( &self, @@ -74,7 +73,7 @@ impl World { ) -> Result<(TerrainChunk, ChunkSupplement), ()> { let mut sampler = self.sample_blocks(); - let chunk_wpos2d = Vec2::from(chunk_pos) * TerrainChunkSize::RECT_SIZE.map(|e| e as i32); + let chunk_wpos2d = chunk_pos * TerrainChunkSize::RECT_SIZE.map(|e| e as i32); let grid_border = 4; let zcache_grid = Grid::populate_from( TerrainChunkSize::RECT_SIZE.map(|e| e as i32) + grid_border * 2, diff --git a/world/src/sim/map.rs b/world/src/sim/map.rs index 6dd4544d80..5365fba293 100644 --- a/world/src/sim/map.rs +++ b/world/src/sim/map.rs @@ -109,7 +109,6 @@ impl MapConfig { /// to the caller to provide a function that translates this information /// into the correct format for a buffer and writes to it. #[allow(clippy::if_same_then_else)] // TODO: Pending review in #587 - #[allow(clippy::useless_conversion)] // TODO: Pending review in #587 #[allow(clippy::unnested_or_patterns)] // TODO: Pending review in #587 #[allow(clippy::many_single_char_names)] pub fn generate( @@ -142,190 +141,186 @@ impl MapConfig { let focus_rect = Vec2::from(focus); let true_sea_level = (CONFIG.sea_level as f64 - focus.z) / gain as f64; - (0..dimensions.y * dimensions.x) - .into_iter() - .for_each(|chunk_idx| { - let i = chunk_idx % dimensions.x as usize; - let j = chunk_idx / dimensions.x as usize; + (0..dimensions.y * dimensions.x).for_each(|chunk_idx| { + let i = chunk_idx % dimensions.x as usize; + let j = chunk_idx / dimensions.x as usize; - let pos = - (focus_rect + Vec2::new(i as f64, j as f64) * scale).map(|e: f64| e as i32); + let pos = (focus_rect + Vec2::new(i as f64, j as f64) * scale).map(|e: f64| e as i32); - let ( - alt, - basement, - water_alt, - humidity, - temperature, - downhill, - river_kind, - is_path, - near_site, - ) = sampler - .get(pos) - .map(|sample| { - ( - sample.alt, - sample.basement, - sample.water_alt, - sample.humidity, - sample.temp, - sample.downhill, - sample.river.river_kind, - sample.path.is_path(), - sample.sites.iter().any(|site| { - site.get_origin() - .distance_squared(pos * TerrainChunkSize::RECT_SIZE.x as i32) - < 64i32.pow(2) - }), - ) - }) - .unwrap_or(( - CONFIG.sea_level, - CONFIG.sea_level, - CONFIG.sea_level, - 0.0, - 0.0, - None, - None, - false, - false, - )); - let humidity = humidity.min(1.0).max(0.0); - let temperature = temperature.min(1.0).max(-1.0) * 0.5 + 0.5; - let pos = pos * TerrainChunkSize::RECT_SIZE.map(|e| e as i32); - let downhill_pos = (downhill - .map(|downhill_pos| downhill_pos) - .unwrap_or(pos + TerrainChunkSize::RECT_SIZE.map(|e| e as i32)) - - pos) - + pos; - let downhill_alt = sampler - .get_wpos(downhill_pos) - .map(|s| if is_basement { s.basement } else { s.alt }) - .unwrap_or(CONFIG.sea_level); - let alt = if is_basement { basement } else { alt }; - let cross_pos = pos - + ((downhill_pos - pos) - .map(|e| e as f32) - .rotated_z(f32::consts::FRAC_PI_2) - .map(|e| e as i32)); - let cross_alt = sampler - .get_wpos(cross_pos) - .map(|s| if is_basement { s.basement } else { s.alt }) - .unwrap_or(CONFIG.sea_level); - // Pointing downhill, forward - // (index--note that (0,0,1) is backward right-handed) - let forward_vec = Vec3::new( - (downhill_pos.x - pos.x) as f64, - (downhill_alt - alt) as f64 * lgain, - (downhill_pos.y - pos.y) as f64, - ); - // Pointing 90 degrees left (in horizontal xy) of downhill, up - // (middle--note that (1,0,0), 90 degrees CCW backward, is right right-handed) - let up_vec = Vec3::new( - (cross_pos.x - pos.x) as f64, - (cross_alt - alt) as f64 * lgain, - (cross_pos.y - pos.y) as f64, - ); - // Then cross points "to the right" (upwards) on a right-handed coordinate - // system. (right-handed coordinate system means (0, 0, 1.0) is - // "forward" into the screen). - let surface_normal = forward_vec.cross(up_vec).normalized(); - let light = (surface_normal.dot(light) + 1.0) / 2.0; - let light = (light * 0.9) + 0.1; + let ( + alt, + basement, + water_alt, + humidity, + temperature, + downhill, + river_kind, + is_path, + near_site, + ) = sampler + .get(pos) + .map(|sample| { + ( + sample.alt, + sample.basement, + sample.water_alt, + sample.humidity, + sample.temp, + sample.downhill, + sample.river.river_kind, + sample.path.is_path(), + sample.sites.iter().any(|site| { + site.get_origin() + .distance_squared(pos * TerrainChunkSize::RECT_SIZE.x as i32) + < 64i32.pow(2) + }), + ) + }) + .unwrap_or(( + CONFIG.sea_level, + CONFIG.sea_level, + CONFIG.sea_level, + 0.0, + 0.0, + None, + None, + false, + false, + )); + let humidity = humidity.min(1.0).max(0.0); + let temperature = temperature.min(1.0).max(-1.0) * 0.5 + 0.5; + let pos = pos * TerrainChunkSize::RECT_SIZE.map(|e| e as i32); + let downhill_pos = (downhill + .map(|downhill_pos| downhill_pos) + .unwrap_or(pos + TerrainChunkSize::RECT_SIZE.map(|e| e as i32)) + - pos) + + pos; + let downhill_alt = sampler + .get_wpos(downhill_pos) + .map(|s| if is_basement { s.basement } else { s.alt }) + .unwrap_or(CONFIG.sea_level); + let alt = if is_basement { basement } else { alt }; + let cross_pos = pos + + ((downhill_pos - pos) + .map(|e| e as f32) + .rotated_z(f32::consts::FRAC_PI_2) + .map(|e| e as i32)); + let cross_alt = sampler + .get_wpos(cross_pos) + .map(|s| if is_basement { s.basement } else { s.alt }) + .unwrap_or(CONFIG.sea_level); + // Pointing downhill, forward + // (index--note that (0,0,1) is backward right-handed) + let forward_vec = Vec3::new( + (downhill_pos.x - pos.x) as f64, + (downhill_alt - alt) as f64 * lgain, + (downhill_pos.y - pos.y) as f64, + ); + // Pointing 90 degrees left (in horizontal xy) of downhill, up + // (middle--note that (1,0,0), 90 degrees CCW backward, is right right-handed) + let up_vec = Vec3::new( + (cross_pos.x - pos.x) as f64, + (cross_alt - alt) as f64 * lgain, + (cross_pos.y - pos.y) as f64, + ); + // Then cross points "to the right" (upwards) on a right-handed coordinate + // system. (right-handed coordinate system means (0, 0, 1.0) is + // "forward" into the screen). + let surface_normal = forward_vec.cross(up_vec).normalized(); + let light = (surface_normal.dot(light) + 1.0) / 2.0; + let light = (light * 0.9) + 0.1; - let true_water_alt = (alt.max(water_alt) as f64 - focus.z) / gain as f64; - let true_alt = (alt as f64 - focus.z) / gain as f64; - let water_depth = (true_water_alt - true_alt).min(1.0).max(0.0); - let water_alt = true_water_alt.min(1.0).max(0.0); - let alt = true_alt.min(1.0).max(0.0); - if is_debug { - let quad = |x: f32| { - ((x as f64 * QUADRANTS as f64).floor() as usize).min(QUADRANTS - 1) - }; - if river_kind.is_none() || humidity != 0.0 { - quads[quad(humidity)][quad(temperature)] += 1; - } - match river_kind { - Some(RiverKind::River { .. }) => { - rivers += 1; - }, - Some(RiverKind::Lake { .. }) => { - lakes += 1; - }, - Some(RiverKind::Ocean { .. }) => { - oceans += 1; - }, - None => {}, - } + let true_water_alt = (alt.max(water_alt) as f64 - focus.z) / gain as f64; + let true_alt = (alt as f64 - focus.z) / gain as f64; + let water_depth = (true_water_alt - true_alt).min(1.0).max(0.0); + let water_alt = true_water_alt.min(1.0).max(0.0); + let alt = true_alt.min(1.0).max(0.0); + if is_debug { + let quad = + |x: f32| ((x as f64 * QUADRANTS as f64).floor() as usize).min(QUADRANTS - 1); + if river_kind.is_none() || humidity != 0.0 { + quads[quad(humidity)][quad(temperature)] += 1; } - - let water_color_factor = 2.0; - let g_water = 32.0 * water_color_factor; - let b_water = 64.0 * water_color_factor; - let rgba = match (river_kind, (is_water, true_alt >= true_sea_level)) { - (_, (false, _)) | (None, (_, true)) => { - let (r, g, b) = ( - (if is_shaded { alt } else { alt } - * if is_temperature { - temperature as f64 - } else if is_shaded { - alt - } else { - 0.0 - }) - .sqrt(), - if is_shaded { 0.4 + (alt * 0.6) } else { alt }, - (if is_shaded { alt } else { alt } - * if is_humidity { - humidity as f64 - } else if is_shaded { - alt - } else { - 0.0 - }) - .sqrt(), - ); - let light = if is_shaded { light } else { 1.0 }; - ( - (r * light * 255.0) as u8, - (g * light * 255.0) as u8, - (b * light * 255.0) as u8, - 255, - ) + match river_kind { + Some(RiverKind::River { .. }) => { + rivers += 1; }, - (Some(RiverKind::Ocean), _) => ( - 0, - ((g_water - water_depth * g_water) * 1.0) as u8, - ((b_water - water_depth * b_water) * 1.0) as u8, - 255, - ), - (Some(RiverKind::River { .. }), _) => ( - 0, - g_water as u8 + (alt * (127.0 - g_water)) as u8, - b_water as u8 + (alt * (255.0 - b_water)) as u8, - 255, - ), - (None, _) | (Some(RiverKind::Lake { .. }), _) => ( - 0, - (((g_water + water_alt * (127.0 - 32.0)) + (-water_depth * g_water)) * 1.0) - as u8, - (((b_water + water_alt * (255.0 - b_water)) + (-water_depth * b_water)) - * 1.0) as u8, - 255, - ), - }; + Some(RiverKind::Lake { .. }) => { + lakes += 1; + }, + Some(RiverKind::Ocean { .. }) => { + oceans += 1; + }, + None => {}, + } + } - let rgba = if near_site { - (0x57, 0x39, 0x33, 0xFF) - } else if is_path { - (0x37, 0x29, 0x23, 0xFF) - } else { - rgba - }; + let water_color_factor = 2.0; + let g_water = 32.0 * water_color_factor; + let b_water = 64.0 * water_color_factor; + let rgba = match (river_kind, (is_water, true_alt >= true_sea_level)) { + (_, (false, _)) | (None, (_, true)) => { + let (r, g, b) = ( + (if is_shaded { alt } else { alt } + * if is_temperature { + temperature as f64 + } else if is_shaded { + alt + } else { + 0.0 + }) + .sqrt(), + if is_shaded { 0.4 + (alt * 0.6) } else { alt }, + (if is_shaded { alt } else { alt } + * if is_humidity { + humidity as f64 + } else if is_shaded { + alt + } else { + 0.0 + }) + .sqrt(), + ); + let light = if is_shaded { light } else { 1.0 }; + ( + (r * light * 255.0) as u8, + (g * light * 255.0) as u8, + (b * light * 255.0) as u8, + 255, + ) + }, + (Some(RiverKind::Ocean), _) => ( + 0, + ((g_water - water_depth * g_water) * 1.0) as u8, + ((b_water - water_depth * b_water) * 1.0) as u8, + 255, + ), + (Some(RiverKind::River { .. }), _) => ( + 0, + g_water as u8 + (alt * (127.0 - g_water)) as u8, + b_water as u8 + (alt * (255.0 - b_water)) as u8, + 255, + ), + (None, _) | (Some(RiverKind::Lake { .. }), _) => ( + 0, + (((g_water + water_alt * (127.0 - 32.0)) + (-water_depth * g_water)) * 1.0) + as u8, + (((b_water + water_alt * (255.0 - b_water)) + (-water_depth * b_water)) * 1.0) + as u8, + 255, + ), + }; - write_pixel(Vec2::new(i, j), rgba); - }); + let rgba = if near_site { + (0x57, 0x39, 0x33, 0xFF) + } else if is_path { + (0x37, 0x29, 0x23, 0xFF) + } else { + rgba + }; + + write_pixel(Vec2::new(i, j), rgba); + }); MapDebug { quads, From c74e5e4b47fe5c918c643d9f80f5c053ddf6c5b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=A4rtens?= Date: Mon, 13 Jul 2020 23:13:54 +0200 Subject: [PATCH 27/85] Changes requested in rewiew --- network/examples/chat/src/main.rs | 10 +++++----- server/src/events/player.rs | 2 +- voxygen/src/menu/main/client_init.rs | 13 +++---------- 3 files changed, 9 insertions(+), 16 deletions(-) diff --git a/network/examples/chat/src/main.rs b/network/examples/chat/src/main.rs index 4d9f08a8f3..f8b93b6581 100644 --- a/network/examples/chat/src/main.rs +++ b/network/examples/chat/src/main.rs @@ -8,7 +8,7 @@ use async_std::sync::RwLock; use clap::{App, Arg}; use futures::executor::{block_on, ThreadPool}; use network::{ProtocolAddr, Network, Participant, Pid, PROMISES_CONSISTENCY, PROMISES_ORDERED}; -use std::{sync::Arc, thread, time::Duration, collections::HashMap}; +use std::{sync::Arc, thread, time::Duration}; use tracing::*; use tracing_subscriber::EnvFilter; @@ -104,19 +104,19 @@ fn server(address: ProtocolAddr) { let server = Arc::new(server); std::thread::spawn(f); let pool = ThreadPool::new().unwrap(); - let participants = Arc::new(RwLock::new(HashMap::new())); + let participants = Arc::new(RwLock::new(Vec::new())); block_on(async { server.listen(address).await.unwrap(); loop { let p1 = Arc::new(server.connected().await.unwrap()); let server1 = server.clone(); - participants.write().await.insert(p1.remote_pid(), p1.clone()); + participants.write().await.push(p1.clone()); pool.spawn_ok(client_connection(server1, p1, participants.clone())); } }); } -async fn client_connection(_network: Arc, participant: Arc, participants: Arc>>>) { +async fn client_connection(_network: Arc, participant: Arc, participants: Arc>>>) { let mut s1 = participant.opened().await.unwrap(); let username = s1.recv::().await.unwrap(); println!("[{}] connected", username); @@ -127,7 +127,7 @@ async fn client_connection(_network: Arc, participant: Arc }, Ok(msg) => { println!("[{}]: {}", username, msg); - for (_, p) in participants.read().await.iter() { + for p in participants.read().await.iter() { match p .open(32, PROMISES_ORDERED | PROMISES_CONSISTENCY) .await { diff --git a/server/src/events/player.rs b/server/src/events/player.rs index c01afd405e..b44f61d7f4 100644 --- a/server/src/events/player.rs +++ b/server/src/events/player.rs @@ -48,7 +48,7 @@ pub fn handle_exit_ingame(server: &mut Server, entity: EcsEntity) { pub fn handle_client_disconnect(server: &mut Server, entity: EcsEntity) -> Event { if let Some(client) = server.state().read_storage::().get(entity) { - trace!("closing participant of client"); + trace!("Closing participant of client"); let participant = client.participant.lock().unwrap().take().unwrap(); if let Err(e) = block_on(participant.disconnect()) { debug!( diff --git a/voxygen/src/menu/main/client_init.rs b/voxygen/src/menu/main/client_init.rs index 05e6281e58..f3ec5fdb2d 100644 --- a/voxygen/src/menu/main/client_init.rs +++ b/voxygen/src/menu/main/client_init.rs @@ -101,24 +101,17 @@ impl ClientInit { }, Err(ClientError::NetworkErr(NetworkError::ConnectFailed(e))) => { if e.kind() == std::io::ErrorKind::PermissionDenied { - warn!( - ?e, - "You can't connect to the server, you are running a \ - incompatible version than the server" - ); + warn!(?e, "Cannot connect to server: Incompatible version"); last_err = Some(Error::ClientError( ClientError::NetworkErr(NetworkError::ConnectFailed(e)), )); break 'tries; } else { - debug!( - "can't reach the server, going to retry in a few \ - seconds" - ); + debug!("Cannot connect to server: Timeout (retrying...)"); } }, Err(e) => { - trace!(?e, "stopping connecting to server, due to error"); + trace!(?e, "Aborting server connection attempt"); last_err = Some(Error::ClientError(e)); break 'tries; }, From 3e5c3de2ac1d46ff8be505802c4b4996c4cdfee3 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Mon, 13 Jul 2020 23:23:44 +0100 Subject: [PATCH 28/85] Neater compass --- common/src/path.rs | 50 ++++++++++++++++++++++---------------- common/src/spiral.rs | 1 + common/src/sys/agent.rs | 24 ++++++++++-------- voxygen/src/hud/minimap.rs | 2 +- 4 files changed, 45 insertions(+), 32 deletions(-) diff --git a/common/src/path.rs b/common/src/path.rs index cf8e6b8e73..d6c10bed98 100644 --- a/common/src/path.rs +++ b/common/src/path.rs @@ -56,6 +56,18 @@ impl From>> for Route { fn from(path: Path>) -> Self { Self { path, next_idx: 0 } } } +pub struct TraversalConfig { + /// The distance to a node at which node is considered visited. + pub node_tolerance: f32, + /// The slowdown factor when following corners. + /// 0.0 = no slowdown on corners, 1.0 = total slowdown on corners. + pub slow_factor: f32, + /// Whether the agent is currently on the ground. + pub on_ground: bool, + /// The distance to the target below which it is considered reached. + pub min_tgt_dist: f32, +} + impl Route { pub fn path(&self) -> &Path> { &self.path } @@ -70,9 +82,7 @@ impl Route { vol: &V, pos: Vec3, vel: Vec3, - on_ground: bool, - traversal_tolerance: f32, - slow_factor: f32, + traversal_cfg: TraversalConfig, ) -> Option<(Vec3, f32)> where V: BaseVol + ReadVol, @@ -120,8 +130,8 @@ impl Route { // Determine whether we're close enough to the next to to consider it completed let dist_sqrd = pos.xy().distance_squared(closest_tgt.xy()); - if dist_sqrd < traversal_tolerance.powf(2.0) * if be_precise { 0.25 } else { 1.0 } - && (pos.z - closest_tgt.z > 1.2 || (pos.z - closest_tgt.z > -0.2 && on_ground)) + if dist_sqrd < traversal_cfg.node_tolerance.powf(2.0) * if be_precise { 0.25 } else { 1.0 } + && (pos.z - closest_tgt.z > 1.2 || (pos.z - closest_tgt.z > -0.2 && traversal_cfg.on_ground)) && (pos.z - closest_tgt.z < 1.2 || (pos.z - closest_tgt.z < 2.9 && vel.z < -0.05)) && vel.z <= 0.0 // Only consider the node reached if there's nothing solid between us and it @@ -242,7 +252,7 @@ impl Route { let bez = CubicBezier2 { start: pos.xy(), - ctrl0: pos.xy() + vel.xy().try_normalized().unwrap_or(Vec2::zero()) * 1.0, + ctrl0: pos.xy() + vel.xy().try_normalized().unwrap_or_default() * 1.0, ctrl1: align(next0, 1.0), end: align(next1, 1.0), }; @@ -254,7 +264,7 @@ impl Route { let next_dir = bez .evaluate_derivative(0.85) .try_normalized() - .unwrap_or(Vec2::zero()); + .unwrap_or_default(); let straight_factor = next_dir .dot(vel.xy().try_normalized().unwrap_or(next_dir)) .max(0.0) @@ -262,15 +272,14 @@ impl Route { let bez = CubicBezier2 { start: pos.xy(), - ctrl0: pos.xy() + vel.xy().try_normalized().unwrap_or(Vec2::zero()) * 1.0, + ctrl0: pos.xy() + vel.xy().try_normalized().unwrap_or_default() * 1.0, ctrl1: align( next0, - (1.0 - straight_factor - * if (next0.z as f32 - pos.z).abs() < 0.25 && !be_precise { - 1.0 - } else { - 0.0 - }) + (1.0 - if (next0.z as f32 - pos.z).abs() < 0.25 && !be_precise { + straight_factor + } else { + 0.0 + }) .max(0.1), ), end: align(next1, 1.0), @@ -292,7 +301,7 @@ impl Route { // Control the entity's speed to hopefully stop us falling off walls on sharp corners. // This code is very imperfect: it does its best but it can still fail for particularly // fast entities. - straight_factor * slow_factor + (1.0 - slow_factor), + straight_factor * traversal_cfg.slow_factor + (1.0 - traversal_cfg.slow_factor), )) .filter(|(bearing, _)| bearing.z < 2.1) } @@ -317,11 +326,8 @@ impl Chaser { vol: &V, pos: Vec3, vel: Vec3, - on_ground: bool, tgt: Vec3, - min_dist: f32, - traversal_tolerance: f32, - slow_factor: f32, + traversal_cfg: TraversalConfig, ) -> Option<(Vec3, f32)> where V: BaseVol + ReadVol, @@ -329,7 +335,9 @@ impl Chaser { let pos_to_tgt = pos.distance(tgt); // If we're already close to the target then there's nothing to do - if ((pos - tgt) * Vec3::new(1.0, 1.0, 2.0)).magnitude_squared() < min_dist.powf(2.0) { + if ((pos - tgt) * Vec3::new(1.0, 1.0, 2.0)).magnitude_squared() + < traversal_cfg.min_tgt_dist.powf(2.0) + { self.route = None; return None; } @@ -349,7 +357,7 @@ impl Chaser { } else { self.route .as_mut() - .and_then(|r| r.traverse(vol, pos, vel, on_ground, traversal_tolerance, slow_factor)) + .and_then(|r| r.traverse(vol, pos, vel, traversal_cfg)) // In theory this filter isn't needed, but in practice agents often try to take // stale paths that start elsewhere. This code makes sure that we're only using // paths that start near us, avoiding the agent doubling back to chase a stale diff --git a/common/src/spiral.rs b/common/src/spiral.rs index 5a13b93d9d..2a76a3484d 100644 --- a/common/src/spiral.rs +++ b/common/src/spiral.rs @@ -16,6 +16,7 @@ impl Spiral2d { impl Iterator for Spiral2d { type Item = Vec2; + #[allow(clippy::erasing_op)] fn next(&mut self) -> Option { let layer_size = (self.layer * 8 + 4 * self.layer.min(1) - 4).max(1); if self.i >= layer_size { diff --git a/common/src/sys/agent.rs b/common/src/sys/agent.rs index aa6159f0a9..36b8afd4e9 100644 --- a/common/src/sys/agent.rs +++ b/common/src/sys/agent.rs @@ -7,7 +7,7 @@ use crate::{ MountState, Ori, PhysicsState, Pos, Scale, Stats, Vel, }, event::{EventBus, ServerEvent}, - path::Chaser, + path::{Chaser, TraversalConfig}, state::{DeltaTime, Time}, sync::{Uid, UidAllocator}, terrain::TerrainGrid, @@ -134,7 +134,7 @@ impl<'a> System<'a> for Sys { // and so can afford to be less precise when trying to move around // the world (especially since they would otherwise get stuck on // obstacles that smaller entities would not). - let traversal_tolerance = scale + vel.0.xy().magnitude() * 0.2; + let node_tolerance = scale + vel.0.xy().magnitude() * 0.2; let slow_factor = body.map(|b| b.base_accel() / 250.0).unwrap_or(0.0).min(1.0); let mut do_idle = false; @@ -207,11 +207,13 @@ impl<'a> System<'a> for Sys { &*terrain, pos.0, vel.0, - physics_state.on_ground, tgt_pos.0, - AVG_FOLLOW_DIST, - traversal_tolerance, - slow_factor, + TraversalConfig { + node_tolerance, + slow_factor, + on_ground: physics_state.on_ground, + min_tgt_dist: AVG_FOLLOW_DIST, + }, ) { inputs.move_dir = bearing.xy().try_normalized().unwrap_or(Vec2::zero()) @@ -325,11 +327,13 @@ impl<'a> System<'a> for Sys { &*terrain, pos.0, vel.0, - physics_state.on_ground, tgt_pos.0, - 1.25, - traversal_tolerance, - slow_factor, + TraversalConfig { + node_tolerance, + slow_factor, + on_ground: physics_state.on_ground, + min_tgt_dist: 1.25, + }, ) { inputs.move_dir = Vec2::from(bearing) .try_normalized() diff --git a/voxygen/src/hud/minimap.rs b/voxygen/src/hud/minimap.rs index c47a2aa420..b696c2c66b 100644 --- a/voxygen/src/hud/minimap.rs +++ b/voxygen/src/hud/minimap.rs @@ -234,7 +234,7 @@ impl<'a> Widget for MiniMap<'a> { + Vec2::unit_y().rotated_z(self.ori.x as f64) * dir.y; let clamped = (cardinal_dir * 3.0) / (cardinal_dir * 3.0).map(|e| e.abs()).reduce_partial_max(); - let pos = clamped * (map_size * 0.75 - 10.0); + let pos = clamped * (map_size * 0.73 - 10.0); Text::new(name) .x_y_position_relative_to( state.ids.grid, From 8d6b442193fad88d7ca117df98592b4cdc21e3b6 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Tue, 14 Jul 2020 20:11:39 +0000 Subject: [PATCH 29/85] Crafting --- CHANGELOG.md | 1 + .../items/{ => consumable}/potion_big.ron | 3 +- .../items/{ => consumable}/potion_med.ron | 2 +- .../items/{ => consumable}/potion_minor.ron | 0 .../common/items/crafting_ing/empty_vial.ron | 7 + .../items/crafting_ing/leather_scraps.ron | 7 + .../common/items/crafting_ing/shiny_gem.ron | 7 + assets/common/items/crafting_ing/stones.ron | 7 + assets/common/items/crafting_ing/twigs.ron | 7 + .../items/crafting_tools/craftsman_hammer.ron | 7 + .../items/crafting_tools/mortar_pestle.ron | 7 + assets/common/items/debug/empty_vial.ron | 7 + assets/common/items/debug/leather_scraps.ron | 7 + assets/common/items/debug/shiny_gem.ron | 7 + assets/common/items/debug/stones.ron | 7 + assets/common/items/debug/twigs.ron | 7 + assets/common/items/{ => food}/apple.ron | 0 .../items/food/apple_mushroom_curry.ron | 11 + assets/common/items/food/apple_stick.ron | 11 + assets/common/items/{ => food}/cheese.ron | 0 assets/common/items/{ => food}/coconut.ron | 0 assets/common/items/{ => food}/mushroom.ron | 0 assets/common/items/food/mushroom_stick.ron | 11 + assets/common/items/{ => ore}/velorite.ron | 0 .../common/items/{ => ore}/veloritefrag.ron | 0 assets/common/items/{ => utility}/bomb.ron | 0 .../common/items/{ => utility}/bomb_pile.ron | 0 assets/common/items/{ => utility}/collar.ron | 0 .../items/{ => utility}/training_dummy.ron | 0 assets/common/loot_table.ron | 27 +- assets/common/recipe_book.ron | 12 + assets/voxygen/audio/sfx/crafting/hammer.wav | 3 + assets/voxygen/background/bg_8.png | 4 +- assets/voxygen/element/animation/gears/1.png | 3 + assets/voxygen/element/animation/gears/2.png | 3 + assets/voxygen/element/animation/gears/3.png | 3 + assets/voxygen/element/animation/gears/4.png | 3 + assets/voxygen/element/animation/gears/5.png | 3 + assets/voxygen/element/buttons/anvil.png | 3 + .../voxygen/element/buttons/anvil_hover.png | 3 + .../voxygen/element/buttons/anvil_press.png | 3 + assets/voxygen/element/frames/selection.png | 3 + assets/voxygen/element/frames/selection.vox | 3 - .../element/frames/selection_frame.png | 3 + .../element/frames/selection_frame.vox | 3 - .../element/frames/selection_hover.png | 3 + .../element/frames/selection_hover.vox | 3 - .../element/frames/selection_press.png | 3 + .../element/frames/selection_press.vox | 3 - .../element/frames/tt_test_corner_tr.png | 3 + .../element/frames/tt_test_corner_tr.vox | 3 - .../voxygen/element/frames/tt_test_edge.png | 3 + .../voxygen/element/frames/tt_test_edge.vox | 3 - assets/voxygen/element/icons/2hsword_m1.png | 4 +- assets/voxygen/element/icons/2hsword_m2.png | 4 +- .../voxygen/element/icons/2hsword_slash.png | 3 - assets/voxygen/element/icons/anvil.png | 3 + assets/voxygen/element/icons/bow_aoe.png | 3 + assets/voxygen/element/icons/bow_m1.png | 4 +- assets/voxygen/element/icons/bow_m2.png | 4 +- assets/voxygen/element/icons/gem.png | 3 + assets/voxygen/element/icons/heal_0.png | 4 +- assets/voxygen/element/icons/item_apple.png | 3 + assets/voxygen/element/icons/item_apple.vox | 3 - .../element/icons/item_apple_curry.png | 3 + .../element/icons/item_apple_stick.png | 3 + assets/voxygen/element/icons/item_cheese.png | 4 +- assets/voxygen/element/icons/item_cheese.vox | 3 - .../voxygen/element/icons/item_leather0.png | 3 + .../element/icons/item_mortarpestlecoco.png | 3 + .../element/icons/item_shroom_stick.png | 3 + .../voxygen/element/icons/skill_charge_3.png | 3 - .../element/icons/skill_sword_pierce.png | 3 + assets/voxygen/element/icons/staff_m1.png | 4 +- assets/voxygen/element/icons/staff_m2.png | 4 +- assets/voxygen/element/misc_bg/crafting.png | 3 + .../element/misc_bg/crafting_frame.png | 3 + assets/voxygen/element/misc_bg/inv_bg.png | 4 +- assets/voxygen/element/misc_bg/map_bg.png | 4 +- assets/voxygen/element/misc_bg/window.png | 3 + assets/voxygen/i18n/de_DE.ron | 8 + assets/voxygen/i18n/en.ron | 10 +- assets/voxygen/item_image_manifest.ron | 57 +- .../voxel/humanoid_main_weapon_manifest.ron | 4 + assets/voxygen/voxel/object/potion_empty.vox | 3 + assets/voxygen/voxel/sprite/gem/gem_blue.vox | 3 + assets/voxygen/voxel/sprite/gem/gem_green.vox | 3 + assets/voxygen/voxel/sprite/gem/gem_red.vox | 3 + assets/voxygen/voxel/sprite/rocks/rock-0.vox | 3 + assets/voxygen/voxel/sprite/rocks/rock-1.vox | 3 + assets/voxygen/voxel/sprite/rocks/rock-2.vox | 3 + assets/voxygen/voxel/sprite/twigs/twigs-0.vox | 3 + assets/voxygen/voxel/sprite/twigs/twigs-1.vox | 3 + assets/voxygen/voxel/sprite/twigs/twigs-2.vox | 3 + .../voxygen/voxel/weapon/hammer/craftsman.vox | 3 + client/src/lib.rs | 56 +- common/src/assets/mod.rs | 5 +- common/src/comp/controller.rs | 1 + common/src/comp/inventory/item/armor.rs | 9 + common/src/comp/inventory/item/mod.rs | 62 ++- common/src/comp/inventory/item/tool.rs | 7 + common/src/comp/inventory/mod.rs | 62 ++- common/src/lib.rs | 1 + common/src/msg/server.rs | 5 +- common/src/recipe.rs | 99 ++++ common/src/terrain/block.rs | 15 + server/src/events/inventory_manip.rs | 49 +- server/src/lib.rs | 2 + voxygen/src/hud/bag.rs | 1 - voxygen/src/hud/buttons.rs | 43 ++ voxygen/src/hud/crafting.rs | 505 ++++++++++++++++++ voxygen/src/hud/img_ids.rs | 25 +- voxygen/src/hud/minimap.rs | 2 +- voxygen/src/hud/mod.rs | 113 +++- voxygen/src/hud/skillbar.rs | 34 +- voxygen/src/menu/char_selection/ui.rs | 9 +- voxygen/src/menu/main/ui.rs | 29 +- voxygen/src/scene/terrain.rs | 87 +++ voxygen/src/session.rs | 3 + voxygen/src/settings.rs | 63 ++- voxygen/src/window.rs | 2 + world/src/block/mod.rs | 5 +- 122 files changed, 1545 insertions(+), 181 deletions(-) rename assets/common/items/{ => consumable}/potion_big.ron (85%) rename assets/common/items/{ => consumable}/potion_med.ron (89%) rename assets/common/items/{ => consumable}/potion_minor.ron (100%) create mode 100644 assets/common/items/crafting_ing/empty_vial.ron create mode 100644 assets/common/items/crafting_ing/leather_scraps.ron create mode 100644 assets/common/items/crafting_ing/shiny_gem.ron create mode 100644 assets/common/items/crafting_ing/stones.ron create mode 100644 assets/common/items/crafting_ing/twigs.ron create mode 100644 assets/common/items/crafting_tools/craftsman_hammer.ron create mode 100644 assets/common/items/crafting_tools/mortar_pestle.ron create mode 100644 assets/common/items/debug/empty_vial.ron create mode 100644 assets/common/items/debug/leather_scraps.ron create mode 100644 assets/common/items/debug/shiny_gem.ron create mode 100644 assets/common/items/debug/stones.ron create mode 100644 assets/common/items/debug/twigs.ron rename assets/common/items/{ => food}/apple.ron (100%) create mode 100644 assets/common/items/food/apple_mushroom_curry.ron create mode 100644 assets/common/items/food/apple_stick.ron rename assets/common/items/{ => food}/cheese.ron (100%) rename assets/common/items/{ => food}/coconut.ron (100%) rename assets/common/items/{ => food}/mushroom.ron (100%) create mode 100644 assets/common/items/food/mushroom_stick.ron rename assets/common/items/{ => ore}/velorite.ron (100%) rename assets/common/items/{ => ore}/veloritefrag.ron (100%) rename assets/common/items/{ => utility}/bomb.ron (100%) rename assets/common/items/{ => utility}/bomb_pile.ron (100%) rename assets/common/items/{ => utility}/collar.ron (100%) rename assets/common/items/{ => utility}/training_dummy.ron (100%) create mode 100644 assets/common/recipe_book.ron create mode 100644 assets/voxygen/audio/sfx/crafting/hammer.wav create mode 100644 assets/voxygen/element/animation/gears/1.png create mode 100644 assets/voxygen/element/animation/gears/2.png create mode 100644 assets/voxygen/element/animation/gears/3.png create mode 100644 assets/voxygen/element/animation/gears/4.png create mode 100644 assets/voxygen/element/animation/gears/5.png create mode 100644 assets/voxygen/element/buttons/anvil.png create mode 100644 assets/voxygen/element/buttons/anvil_hover.png create mode 100644 assets/voxygen/element/buttons/anvil_press.png create mode 100644 assets/voxygen/element/frames/selection.png delete mode 100644 assets/voxygen/element/frames/selection.vox create mode 100644 assets/voxygen/element/frames/selection_frame.png delete mode 100644 assets/voxygen/element/frames/selection_frame.vox create mode 100644 assets/voxygen/element/frames/selection_hover.png delete mode 100644 assets/voxygen/element/frames/selection_hover.vox create mode 100644 assets/voxygen/element/frames/selection_press.png delete mode 100644 assets/voxygen/element/frames/selection_press.vox create mode 100644 assets/voxygen/element/frames/tt_test_corner_tr.png delete mode 100644 assets/voxygen/element/frames/tt_test_corner_tr.vox create mode 100644 assets/voxygen/element/frames/tt_test_edge.png delete mode 100644 assets/voxygen/element/frames/tt_test_edge.vox delete mode 100644 assets/voxygen/element/icons/2hsword_slash.png create mode 100644 assets/voxygen/element/icons/anvil.png create mode 100644 assets/voxygen/element/icons/bow_aoe.png create mode 100644 assets/voxygen/element/icons/gem.png create mode 100644 assets/voxygen/element/icons/item_apple.png delete mode 100644 assets/voxygen/element/icons/item_apple.vox create mode 100644 assets/voxygen/element/icons/item_apple_curry.png create mode 100644 assets/voxygen/element/icons/item_apple_stick.png delete mode 100644 assets/voxygen/element/icons/item_cheese.vox create mode 100644 assets/voxygen/element/icons/item_leather0.png create mode 100644 assets/voxygen/element/icons/item_mortarpestlecoco.png create mode 100644 assets/voxygen/element/icons/item_shroom_stick.png delete mode 100644 assets/voxygen/element/icons/skill_charge_3.png create mode 100644 assets/voxygen/element/icons/skill_sword_pierce.png create mode 100644 assets/voxygen/element/misc_bg/crafting.png create mode 100644 assets/voxygen/element/misc_bg/crafting_frame.png create mode 100644 assets/voxygen/element/misc_bg/window.png create mode 100644 assets/voxygen/voxel/object/potion_empty.vox create mode 100644 assets/voxygen/voxel/sprite/gem/gem_blue.vox create mode 100644 assets/voxygen/voxel/sprite/gem/gem_green.vox create mode 100644 assets/voxygen/voxel/sprite/gem/gem_red.vox create mode 100644 assets/voxygen/voxel/sprite/rocks/rock-0.vox create mode 100644 assets/voxygen/voxel/sprite/rocks/rock-1.vox create mode 100644 assets/voxygen/voxel/sprite/rocks/rock-2.vox create mode 100644 assets/voxygen/voxel/sprite/twigs/twigs-0.vox create mode 100644 assets/voxygen/voxel/sprite/twigs/twigs-1.vox create mode 100644 assets/voxygen/voxel/sprite/twigs/twigs-2.vox create mode 100644 assets/voxygen/voxel/weapon/hammer/craftsman.vox create mode 100644 common/src/recipe.rs create mode 100644 voxygen/src/hud/crafting.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 66cec07206..94cc10600e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,6 +43,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added spin attack for axe - Creature specific stats - Minimap compass +- Initial crafting system implementation ### Changed diff --git a/assets/common/items/potion_big.ron b/assets/common/items/consumable/potion_big.ron similarity index 85% rename from assets/common/items/potion_big.ron rename to assets/common/items/consumable/potion_big.ron index 42553d58e2..60ddf64298 100644 --- a/assets/common/items/potion_big.ron +++ b/assets/common/items/consumable/potion_big.ron @@ -2,11 +2,10 @@ Item( name: "Large Potion", description: "Restores 100 Health\n\n", kind: Consumable( - kind: PotionMinor, + kind: PotionLarge, effect: Health(( amount: 100, cause: Item, )), - , ), ) diff --git a/assets/common/items/potion_med.ron b/assets/common/items/consumable/potion_med.ron similarity index 89% rename from assets/common/items/potion_med.ron rename to assets/common/items/consumable/potion_med.ron index 61cf612204..ece9348e1b 100644 --- a/assets/common/items/potion_med.ron +++ b/assets/common/items/consumable/potion_med.ron @@ -2,7 +2,7 @@ Item( name: "Medium Potion", description: "Restores 70 Health\n\n", kind: Consumable( - kind: PotionMinor, + kind: PotionMed, effect: Health(( amount: 70, cause: Item, diff --git a/assets/common/items/potion_minor.ron b/assets/common/items/consumable/potion_minor.ron similarity index 100% rename from assets/common/items/potion_minor.ron rename to assets/common/items/consumable/potion_minor.ron diff --git a/assets/common/items/crafting_ing/empty_vial.ron b/assets/common/items/crafting_ing/empty_vial.ron new file mode 100644 index 0000000000..422a2870dd --- /dev/null +++ b/assets/common/items/crafting_ing/empty_vial.ron @@ -0,0 +1,7 @@ +Item( + name: "Empty Vial", + description: "Can be filled with fluids.", + kind: Ingredient( + kind: EmptyVial, + ) +) diff --git a/assets/common/items/crafting_ing/leather_scraps.ron b/assets/common/items/crafting_ing/leather_scraps.ron new file mode 100644 index 0000000000..cc546f8639 --- /dev/null +++ b/assets/common/items/crafting_ing/leather_scraps.ron @@ -0,0 +1,7 @@ +Item( + name: "Leather Scraps", + description: "Used to craft various items.", + kind: Ingredient( + kind: LeatherScraps, + ) +) diff --git a/assets/common/items/crafting_ing/shiny_gem.ron b/assets/common/items/crafting_ing/shiny_gem.ron new file mode 100644 index 0000000000..ec396047fa --- /dev/null +++ b/assets/common/items/crafting_ing/shiny_gem.ron @@ -0,0 +1,7 @@ +Item( + name: "Shiny Gem", + description: "It's so shiny!", + kind: Ingredient( + kind: ShinyGem, + ) +) diff --git a/assets/common/items/crafting_ing/stones.ron b/assets/common/items/crafting_ing/stones.ron new file mode 100644 index 0000000000..20d5842255 --- /dev/null +++ b/assets/common/items/crafting_ing/stones.ron @@ -0,0 +1,7 @@ +Item( + name: "Stones", + description: "Pebbles from the ground.", + kind: Ingredient( + kind: Stones, + ) +) diff --git a/assets/common/items/crafting_ing/twigs.ron b/assets/common/items/crafting_ing/twigs.ron new file mode 100644 index 0000000000..3eaba0f24a --- /dev/null +++ b/assets/common/items/crafting_ing/twigs.ron @@ -0,0 +1,7 @@ +Item( + name: "Twigs", + description: "Dry.", + kind: Ingredient( + kind: Twigs, + ) +) diff --git a/assets/common/items/crafting_tools/craftsman_hammer.ron b/assets/common/items/crafting_tools/craftsman_hammer.ron new file mode 100644 index 0000000000..30bdb55506 --- /dev/null +++ b/assets/common/items/crafting_tools/craftsman_hammer.ron @@ -0,0 +1,7 @@ +Item( + name: "Craftsman Hammer", + description: "Used to craft various items.", + kind: Ingredient( + kind: CraftsmanHammer, + ) +) diff --git a/assets/common/items/crafting_tools/mortar_pestle.ron b/assets/common/items/crafting_tools/mortar_pestle.ron new file mode 100644 index 0000000000..d983f15b3f --- /dev/null +++ b/assets/common/items/crafting_tools/mortar_pestle.ron @@ -0,0 +1,7 @@ +Item( + name: "Mortar and Pestle", + description: "Crushes and grinds things into\na fine powder or paste.\nUsed to craft various items.", + kind: Ingredient( + kind: MortarPestle, + ) +) diff --git a/assets/common/items/debug/empty_vial.ron b/assets/common/items/debug/empty_vial.ron new file mode 100644 index 0000000000..422a2870dd --- /dev/null +++ b/assets/common/items/debug/empty_vial.ron @@ -0,0 +1,7 @@ +Item( + name: "Empty Vial", + description: "Can be filled with fluids.", + kind: Ingredient( + kind: EmptyVial, + ) +) diff --git a/assets/common/items/debug/leather_scraps.ron b/assets/common/items/debug/leather_scraps.ron new file mode 100644 index 0000000000..cc546f8639 --- /dev/null +++ b/assets/common/items/debug/leather_scraps.ron @@ -0,0 +1,7 @@ +Item( + name: "Leather Scraps", + description: "Used to craft various items.", + kind: Ingredient( + kind: LeatherScraps, + ) +) diff --git a/assets/common/items/debug/shiny_gem.ron b/assets/common/items/debug/shiny_gem.ron new file mode 100644 index 0000000000..ec396047fa --- /dev/null +++ b/assets/common/items/debug/shiny_gem.ron @@ -0,0 +1,7 @@ +Item( + name: "Shiny Gem", + description: "It's so shiny!", + kind: Ingredient( + kind: ShinyGem, + ) +) diff --git a/assets/common/items/debug/stones.ron b/assets/common/items/debug/stones.ron new file mode 100644 index 0000000000..20d5842255 --- /dev/null +++ b/assets/common/items/debug/stones.ron @@ -0,0 +1,7 @@ +Item( + name: "Stones", + description: "Pebbles from the ground.", + kind: Ingredient( + kind: Stones, + ) +) diff --git a/assets/common/items/debug/twigs.ron b/assets/common/items/debug/twigs.ron new file mode 100644 index 0000000000..3eaba0f24a --- /dev/null +++ b/assets/common/items/debug/twigs.ron @@ -0,0 +1,7 @@ +Item( + name: "Twigs", + description: "Dry.", + kind: Ingredient( + kind: Twigs, + ) +) diff --git a/assets/common/items/apple.ron b/assets/common/items/food/apple.ron similarity index 100% rename from assets/common/items/apple.ron rename to assets/common/items/food/apple.ron diff --git a/assets/common/items/food/apple_mushroom_curry.ron b/assets/common/items/food/apple_mushroom_curry.ron new file mode 100644 index 0000000000..d8146e4c82 --- /dev/null +++ b/assets/common/items/food/apple_mushroom_curry.ron @@ -0,0 +1,11 @@ +Item( + name: "Mushroom Curry", + description: "Restores 120 Health\n\nWho could say no to that?\n\n", + kind: Consumable( + kind: AppleShroomCurry, + effect: Health(( + amount: 120, + cause: Item, + )), + ), +) diff --git a/assets/common/items/food/apple_stick.ron b/assets/common/items/food/apple_stick.ron new file mode 100644 index 0000000000..78bd97ce66 --- /dev/null +++ b/assets/common/items/food/apple_stick.ron @@ -0,0 +1,11 @@ +Item( + name: "Apple Stick", + description: "Restores 60 Health\n\n", + kind: Consumable( + kind: AppleStick, + effect: Health(( + amount: 60, + cause: Item, + )), + ), +) diff --git a/assets/common/items/cheese.ron b/assets/common/items/food/cheese.ron similarity index 100% rename from assets/common/items/cheese.ron rename to assets/common/items/food/cheese.ron diff --git a/assets/common/items/coconut.ron b/assets/common/items/food/coconut.ron similarity index 100% rename from assets/common/items/coconut.ron rename to assets/common/items/food/coconut.ron diff --git a/assets/common/items/mushroom.ron b/assets/common/items/food/mushroom.ron similarity index 100% rename from assets/common/items/mushroom.ron rename to assets/common/items/food/mushroom.ron diff --git a/assets/common/items/food/mushroom_stick.ron b/assets/common/items/food/mushroom_stick.ron new file mode 100644 index 0000000000..d44bbbec48 --- /dev/null +++ b/assets/common/items/food/mushroom_stick.ron @@ -0,0 +1,11 @@ +Item( + name: "Mushroom Stick", + description: "Restores 50 Health\n\n", + kind: Consumable( + kind: MushroomStick, + effect: Health(( + amount: 50, + cause: Item, + )), + ), +) diff --git a/assets/common/items/velorite.ron b/assets/common/items/ore/velorite.ron similarity index 100% rename from assets/common/items/velorite.ron rename to assets/common/items/ore/velorite.ron diff --git a/assets/common/items/veloritefrag.ron b/assets/common/items/ore/veloritefrag.ron similarity index 100% rename from assets/common/items/veloritefrag.ron rename to assets/common/items/ore/veloritefrag.ron diff --git a/assets/common/items/bomb.ron b/assets/common/items/utility/bomb.ron similarity index 100% rename from assets/common/items/bomb.ron rename to assets/common/items/utility/bomb.ron diff --git a/assets/common/items/bomb_pile.ron b/assets/common/items/utility/bomb_pile.ron similarity index 100% rename from assets/common/items/bomb_pile.ron rename to assets/common/items/utility/bomb_pile.ron diff --git a/assets/common/items/collar.ron b/assets/common/items/utility/collar.ron similarity index 100% rename from assets/common/items/collar.ron rename to assets/common/items/utility/collar.ron diff --git a/assets/common/items/training_dummy.ron b/assets/common/items/utility/training_dummy.ron similarity index 100% rename from assets/common/items/training_dummy.ron rename to assets/common/items/utility/training_dummy.ron diff --git a/assets/common/loot_table.ron b/assets/common/loot_table.ron index 61f7d939cd..7358a55729 100644 --- a/assets/common/loot_table.ron +++ b/assets/common/loot_table.ron @@ -1,18 +1,23 @@ [ // All loot rates go here // food - (3, "common.items.cheese"), - (3, "common.items.apple"), - (3, "common.items.mushroom"), + (3, "common.items.food.cheese"), + (3, "common.items.food.apple"), + (3, "common.items.food.mushroom"), + (1, "common.items.food.coconut"), // miscellaneous - (0.4, "common.items.velorite"), - (0.6, "common.items.veloritefrag"), - (0.6, "common.items.cheese"), - (0.6, "common.items.apple"), - (1.5, "common.items.potion_minor"), - (0.5, "common.items.collar"), - (0.5, "common.items.bomb_pile"), - (1, "common.items.bomb"), + (0.4, "common.items.ore.velorite"), + (0.6, "common.items.ore.veloritefrag"), + (0.1, "common.items.consumable.potion_minor"), + (0.01, "common.items.utility.collar"), + (0.01, "common.items.utility.bomb_pile"), + (0.1, "common.items.utility.bomb"), + // crafting ingredients + (0.5, "common.items.crafting_ing.shiny_gem"), + (2, "common.items.crafting_ing.leather_scraps"), + (1, "common.items.crafting_ing.empty_vial"), + (2, "common.items.crafting_ing.stones"), + (3, "common.items.crafting_ing.twigs"), // swords (0.1, "common.items.weapons.sword.starter_sword"), (0.1, "common.items.weapons.sword.wood_sword"), diff --git a/assets/common/recipe_book.ron b/assets/common/recipe_book.ron new file mode 100644 index 0000000000..cda3378172 --- /dev/null +++ b/assets/common/recipe_book.ron @@ -0,0 +1,12 @@ +{ + "crafting_hammer": (("common.items.crafting_tools.craftsman_hammer", 1),[("common.items.crafting_ing.twigs", 10), ("common.items.crafting_ing.stones", 10)]), + "mortar_pestle": (("common.items.crafting_tools.mortar_pestle", 1), [("common.items.crafting_ing.stones", 6), ("common.items.food.coconut", 2), ("common.items.crafting_tools.craftsman_hammer", 0)]), + "velorite_frag": (("common.items.ore.veloritefrag", 2), [("common.items.ore.velorite", 1), ("common.items.crafting_tools.craftsman_hammer", 0)]), + "potion_s": (("common.items.consumable.potion_minor", 1), [("common.items.crafting_ing.empty_vial", 1), ("common.items.ore.veloritefrag", 2)]), + "potion_m": (("common.items.consumable.potion_med", 1), [("common.items.consumable.potion_minor", 2), ("common.items.ore.veloritefrag", 4)]), + "collar_basic": (("common.items.utility.collar", 1), [("common.items.crafting_ing.leather_scraps", 5), ("common.items.crafting_ing.shiny_gem", 1)]), + "bomb_coconut": (("common.items.utility.bomb", 1), [("common.items.crafting_ing.stones", 10), ("common.items.food.coconut", 2), ("common.items.ore.veloritefrag", 2), ("common.items.crafting_tools.mortar_pestle", 0)]), + "apple_shroom_curry": (("common.items.food.apple_mushroom_curry", 1), [("common.items.food.mushroom", 10), ("common.items.food.coconut", 1), ("common.items.food.apple", 5), ("common.items.crafting_tools.mortar_pestle", 0)]), + "apples_stick": (("common.items.food.apple_stick", 1),[("common.items.crafting_ing.twigs", 1), ("common.items.food.apple", 3)]), + "mushroom_stick": (("common.items.food.mushroom_stick", 1),[("common.items.crafting_ing.twigs", 1), ("common.items.food.mushroom", 5)]), +} diff --git a/assets/voxygen/audio/sfx/crafting/hammer.wav b/assets/voxygen/audio/sfx/crafting/hammer.wav new file mode 100644 index 0000000000..03223c4ee4 --- /dev/null +++ b/assets/voxygen/audio/sfx/crafting/hammer.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:be15f45edcf757e9e2135323d5d9d96069022ef21dcff72df80a49637bd31bc3 +size 185678 diff --git a/assets/voxygen/background/bg_8.png b/assets/voxygen/background/bg_8.png index 1688dee237..d429e5eec4 100644 --- a/assets/voxygen/background/bg_8.png +++ b/assets/voxygen/background/bg_8.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:74e8aab0abd64fe190607c87a6e70a3d3b4409947d8698754ff3c31e0e6debc9 -size 403497 +oid sha256:bd6507de7afa3bbc697fb2867fadc44184298dc9026f30fe91613c06dc72723d +size 906501 diff --git a/assets/voxygen/element/animation/gears/1.png b/assets/voxygen/element/animation/gears/1.png new file mode 100644 index 0000000000..fd1946be6b --- /dev/null +++ b/assets/voxygen/element/animation/gears/1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:798293778955ec9bfe8ae9a2fb6de33adff8a1ba457d693689de644f71bc8f9e +size 843 diff --git a/assets/voxygen/element/animation/gears/2.png b/assets/voxygen/element/animation/gears/2.png new file mode 100644 index 0000000000..d248a4b4cf --- /dev/null +++ b/assets/voxygen/element/animation/gears/2.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:63da1eb3c7663a921ef7f0995c8a5beee3121fa8cb2d4d3f50d6c5d8ade6ef64 +size 2067 diff --git a/assets/voxygen/element/animation/gears/3.png b/assets/voxygen/element/animation/gears/3.png new file mode 100644 index 0000000000..f4f76e1fea --- /dev/null +++ b/assets/voxygen/element/animation/gears/3.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5336ccfb5c8668959f376475dd32c48b5850c527da56a7924140027dd904a921 +size 2066 diff --git a/assets/voxygen/element/animation/gears/4.png b/assets/voxygen/element/animation/gears/4.png new file mode 100644 index 0000000000..b70867e7a0 --- /dev/null +++ b/assets/voxygen/element/animation/gears/4.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7cb70f05b0e179d1c99f9878148a8a93f7347e8610a74348e5a3d7100f0303d0 +size 2073 diff --git a/assets/voxygen/element/animation/gears/5.png b/assets/voxygen/element/animation/gears/5.png new file mode 100644 index 0000000000..7652f808cb --- /dev/null +++ b/assets/voxygen/element/animation/gears/5.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3ca60d03c82bc855cfda2e1fb61273f00a355e2c80cc7882c5f79c56c7de67c8 +size 2101 diff --git a/assets/voxygen/element/buttons/anvil.png b/assets/voxygen/element/buttons/anvil.png new file mode 100644 index 0000000000..1782d62f65 --- /dev/null +++ b/assets/voxygen/element/buttons/anvil.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c23cc6edcc9af1e2d82432d9b4f60cbf905cf3d970e6ae467f333cffdcb86e06 +size 355 diff --git a/assets/voxygen/element/buttons/anvil_hover.png b/assets/voxygen/element/buttons/anvil_hover.png new file mode 100644 index 0000000000..e57ba7b1e0 --- /dev/null +++ b/assets/voxygen/element/buttons/anvil_hover.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:be5dd7c186a92d519fcbb991308abb911889c5f830028ae4345f190fd7115a99 +size 437 diff --git a/assets/voxygen/element/buttons/anvil_press.png b/assets/voxygen/element/buttons/anvil_press.png new file mode 100644 index 0000000000..075d36e93b --- /dev/null +++ b/assets/voxygen/element/buttons/anvil_press.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9eccb36375e2ddf203f42809783dfd5382ab59a1cec50e16da85b6b03d8552f3 +size 412 diff --git a/assets/voxygen/element/frames/selection.png b/assets/voxygen/element/frames/selection.png new file mode 100644 index 0000000000..24566a3030 --- /dev/null +++ b/assets/voxygen/element/frames/selection.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:66bb0ef05d8ba8a73c1c49ea12dcd401d52b3c69db312dc5edca91fb79dfad4e +size 261 diff --git a/assets/voxygen/element/frames/selection.vox b/assets/voxygen/element/frames/selection.vox deleted file mode 100644 index dece7feb54..0000000000 --- a/assets/voxygen/element/frames/selection.vox +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9e800abd210e23b0ba3125929dfbaa191de75ee1f6c54e2d9634c56192023ba2 -size 55156 diff --git a/assets/voxygen/element/frames/selection_frame.png b/assets/voxygen/element/frames/selection_frame.png new file mode 100644 index 0000000000..235d84c356 --- /dev/null +++ b/assets/voxygen/element/frames/selection_frame.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:61c241e368224bbbbf010a188308729de733927dd764d727c08c575216016fec +size 184 diff --git a/assets/voxygen/element/frames/selection_frame.vox b/assets/voxygen/element/frames/selection_frame.vox deleted file mode 100644 index b8c6bc32ae..0000000000 --- a/assets/voxygen/element/frames/selection_frame.vox +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:43867e582d418ed695eed2802729b6038e96484473c4b01840256119313c4a36 -size 2288 diff --git a/assets/voxygen/element/frames/selection_hover.png b/assets/voxygen/element/frames/selection_hover.png new file mode 100644 index 0000000000..d72c4bbf53 --- /dev/null +++ b/assets/voxygen/element/frames/selection_hover.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:10239624d63369d53b42af95865ee29bec81157a1b085891554d24309bdf544c +size 259 diff --git a/assets/voxygen/element/frames/selection_hover.vox b/assets/voxygen/element/frames/selection_hover.vox deleted file mode 100644 index 7b806be7a9..0000000000 --- a/assets/voxygen/element/frames/selection_hover.vox +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:6bd73f31f77e3e4d4f65dbff3b37dda8afefbaf21e226a5f7debdaac8439f2f9 -size 66532 diff --git a/assets/voxygen/element/frames/selection_press.png b/assets/voxygen/element/frames/selection_press.png new file mode 100644 index 0000000000..2aa3260773 --- /dev/null +++ b/assets/voxygen/element/frames/selection_press.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9e8d4043fa966b772cfc0222b9996c06407fd3bf756256a4eff3548f2928e2db +size 250 diff --git a/assets/voxygen/element/frames/selection_press.vox b/assets/voxygen/element/frames/selection_press.vox deleted file mode 100644 index e981536ef1..0000000000 --- a/assets/voxygen/element/frames/selection_press.vox +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:beccc5d12589e2467a6803d6e13824a2a5e8e4ff31c0d02e139de7bd0f2a3f99 -size 66532 diff --git a/assets/voxygen/element/frames/tt_test_corner_tr.png b/assets/voxygen/element/frames/tt_test_corner_tr.png new file mode 100644 index 0000000000..a66a135760 --- /dev/null +++ b/assets/voxygen/element/frames/tt_test_corner_tr.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f0de494ff10082d841254a96d3ea5d87d3bc77e2b7e10662584c86957b810460 +size 134 diff --git a/assets/voxygen/element/frames/tt_test_corner_tr.vox b/assets/voxygen/element/frames/tt_test_corner_tr.vox deleted file mode 100644 index f191188d7c..0000000000 --- a/assets/voxygen/element/frames/tt_test_corner_tr.vox +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8d9a0b4cc32a126ec3b31d2074bfaf9a03f6d177632d407df1f115242cfe8e8b -size 56468 diff --git a/assets/voxygen/element/frames/tt_test_edge.png b/assets/voxygen/element/frames/tt_test_edge.png new file mode 100644 index 0000000000..b28d1e380c --- /dev/null +++ b/assets/voxygen/element/frames/tt_test_edge.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3a44ab4a366dd977cd2ece967991b1eb98c271b4c07ee763739af3a50478acb4 +size 117 diff --git a/assets/voxygen/element/frames/tt_test_edge.vox b/assets/voxygen/element/frames/tt_test_edge.vox deleted file mode 100644 index 7a090f9e98..0000000000 --- a/assets/voxygen/element/frames/tt_test_edge.vox +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c7ab1c724d9c0b4b7bc80a4cea39e2b483a50a2291b6d497ed1baa0f8595b43b -size 56220 diff --git a/assets/voxygen/element/icons/2hsword_m1.png b/assets/voxygen/element/icons/2hsword_m1.png index 1b36b8a46f..582a57af16 100644 --- a/assets/voxygen/element/icons/2hsword_m1.png +++ b/assets/voxygen/element/icons/2hsword_m1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f442cb6ef061bfee9e66168b9c32e5bdb7e59fde790c0be31f93785857779e6f -size 784 +oid sha256:3f27d1046c6e1a2832b7f43750f27c2aeeb7f44a874e558b5380bcdde5bfe935 +size 466 diff --git a/assets/voxygen/element/icons/2hsword_m2.png b/assets/voxygen/element/icons/2hsword_m2.png index d54cda0e92..bf991d0c42 100644 --- a/assets/voxygen/element/icons/2hsword_m2.png +++ b/assets/voxygen/element/icons/2hsword_m2.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2b7a2ab9fac21eade39278def1c4e76ce793737c6bd3f4d55037a930ad304e34 -size 929 +oid sha256:9e679ee275cec6cf72962770364d9cf39d164af66ebd03969a0c33fca2dc2ec0 +size 357 diff --git a/assets/voxygen/element/icons/2hsword_slash.png b/assets/voxygen/element/icons/2hsword_slash.png deleted file mode 100644 index 18d36aef24..0000000000 --- a/assets/voxygen/element/icons/2hsword_slash.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b827aa50558455a6ba821e4f032d45ae04d7bfdcecd2597fc1cb6c0b0e6d1973 -size 782 diff --git a/assets/voxygen/element/icons/anvil.png b/assets/voxygen/element/icons/anvil.png new file mode 100644 index 0000000000..d65e6c43d5 --- /dev/null +++ b/assets/voxygen/element/icons/anvil.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a7f9c7eecc22a0be5add0a17f7bdfb0967c7d44b605fd3de1ad29ae07a69851a +size 355 diff --git a/assets/voxygen/element/icons/bow_aoe.png b/assets/voxygen/element/icons/bow_aoe.png new file mode 100644 index 0000000000..9fd92299ab --- /dev/null +++ b/assets/voxygen/element/icons/bow_aoe.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:05080ad4fdb4ad813e7fc21bac32a18a93f5c0cd049b457844ebedca527ae80b +size 248 diff --git a/assets/voxygen/element/icons/bow_m1.png b/assets/voxygen/element/icons/bow_m1.png index b9cb8af63d..126a0acdd5 100644 --- a/assets/voxygen/element/icons/bow_m1.png +++ b/assets/voxygen/element/icons/bow_m1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ff1b52d259aafa8384e05dc5ae2ca3dcd7a88c7a4456149cc7a37e9e0c106ad5 -size 808 +oid sha256:ec6cf7010b6254b782286e087aa84175a78841b89945de90a50f5989e91880d7 +size 317 diff --git a/assets/voxygen/element/icons/bow_m2.png b/assets/voxygen/element/icons/bow_m2.png index 31627cde58..d28141f333 100644 --- a/assets/voxygen/element/icons/bow_m2.png +++ b/assets/voxygen/element/icons/bow_m2.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b580f448d4d3d0f86a5f3382842c6ef29507b7717d5c2994e902a0d9d82d8f0b -size 16863 +oid sha256:c689c982806aeefd1608afc5c8bcdf62873e2d0b6ce63d7296e882126c522b84 +size 275 diff --git a/assets/voxygen/element/icons/gem.png b/assets/voxygen/element/icons/gem.png new file mode 100644 index 0000000000..29f7af2101 --- /dev/null +++ b/assets/voxygen/element/icons/gem.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:52dcdf8dac9caa6c43a10ac35f11439dcc8669f9e34ce8d152ff676f5cf1aaac +size 260 diff --git a/assets/voxygen/element/icons/heal_0.png b/assets/voxygen/element/icons/heal_0.png index bac626be04..e63fee6398 100644 --- a/assets/voxygen/element/icons/heal_0.png +++ b/assets/voxygen/element/icons/heal_0.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e68b8d6a36b444e17797015e439ed711b865f53a1992762139db2228ca25b686 -size 730 +oid sha256:9231d976f42b42f097a2cf247142ecb837869a33ff4451467e018667fd5d76f3 +size 700 diff --git a/assets/voxygen/element/icons/item_apple.png b/assets/voxygen/element/icons/item_apple.png new file mode 100644 index 0000000000..406c97f13a --- /dev/null +++ b/assets/voxygen/element/icons/item_apple.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:01852fbd88c3d390ba33c8aeb64da7eec54eb6ccc798ddc56525ed55ba41f822 +size 338 diff --git a/assets/voxygen/element/icons/item_apple.vox b/assets/voxygen/element/icons/item_apple.vox deleted file mode 100644 index b4a60bd3fb..0000000000 --- a/assets/voxygen/element/icons/item_apple.vox +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:bec86131cd09411c3b1516d602ecf0db3632d7c7ca5db0408743325e8d2ccfff -size 1748 diff --git a/assets/voxygen/element/icons/item_apple_curry.png b/assets/voxygen/element/icons/item_apple_curry.png new file mode 100644 index 0000000000..5b25b38538 --- /dev/null +++ b/assets/voxygen/element/icons/item_apple_curry.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a1dc441e3117b737cb08ab39858c341accbd69916de2c7f539f660a17ed28fee +size 358 diff --git a/assets/voxygen/element/icons/item_apple_stick.png b/assets/voxygen/element/icons/item_apple_stick.png new file mode 100644 index 0000000000..1e0d6b5738 --- /dev/null +++ b/assets/voxygen/element/icons/item_apple_stick.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8f0b96e1f3c109908165dad8646d0a6f5d943e8c380ce8f26fd2ed0ff56f7e4a +size 366 diff --git a/assets/voxygen/element/icons/item_cheese.png b/assets/voxygen/element/icons/item_cheese.png index e20c21aa7e..3975d73506 100644 --- a/assets/voxygen/element/icons/item_cheese.png +++ b/assets/voxygen/element/icons/item_cheese.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6b73e44bddd679b83b8d2e668589e06316b419777c9ac3818c00fdd01a18affe -size 245 +oid sha256:02643618be3bd63bcf69c41eedbecfa20d28fa510683ac6bad6c26cfec66f463 +size 244 diff --git a/assets/voxygen/element/icons/item_cheese.vox b/assets/voxygen/element/icons/item_cheese.vox deleted file mode 100644 index 5bfc15c670..0000000000 --- a/assets/voxygen/element/icons/item_cheese.vox +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f086a745accda8521bcb21215024b40b111bae815b73937d87a8fd838038e61f -size 1680 diff --git a/assets/voxygen/element/icons/item_leather0.png b/assets/voxygen/element/icons/item_leather0.png new file mode 100644 index 0000000000..f8f60e9180 --- /dev/null +++ b/assets/voxygen/element/icons/item_leather0.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ec7cc129c46d1daeee085366798f9df2d7d4525d7ccbd21087466d6d1a25a2bc +size 416 diff --git a/assets/voxygen/element/icons/item_mortarpestlecoco.png b/assets/voxygen/element/icons/item_mortarpestlecoco.png new file mode 100644 index 0000000000..f3612606e4 --- /dev/null +++ b/assets/voxygen/element/icons/item_mortarpestlecoco.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c3de3c3c6ffc2edea096e10a718022583db28f9a64207adc27f06ce3ec162b1f +size 304 diff --git a/assets/voxygen/element/icons/item_shroom_stick.png b/assets/voxygen/element/icons/item_shroom_stick.png new file mode 100644 index 0000000000..e152d4cb6a --- /dev/null +++ b/assets/voxygen/element/icons/item_shroom_stick.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b213392e6df9d7e45c576aa7a6ce846156beae0777b05852fc87c79eaf307d49 +size 348 diff --git a/assets/voxygen/element/icons/skill_charge_3.png b/assets/voxygen/element/icons/skill_charge_3.png deleted file mode 100644 index 82c5cfae2e..0000000000 --- a/assets/voxygen/element/icons/skill_charge_3.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:1c872af7c35dced7c6b11f2838b6ecc2c0176da2f5e3767c2d76b437a9b2e837 -size 1213 diff --git a/assets/voxygen/element/icons/skill_sword_pierce.png b/assets/voxygen/element/icons/skill_sword_pierce.png new file mode 100644 index 0000000000..6ba4b0b090 --- /dev/null +++ b/assets/voxygen/element/icons/skill_sword_pierce.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d14a405f1e2bdbaaa68dff706fd62437de970323e43c9f4f764a2095b524d8fc +size 389 diff --git a/assets/voxygen/element/icons/staff_m1.png b/assets/voxygen/element/icons/staff_m1.png index 3955caf46a..717c22eac6 100644 --- a/assets/voxygen/element/icons/staff_m1.png +++ b/assets/voxygen/element/icons/staff_m1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0b743bbccb12309114e55a35d21b196f6a97b458d4ea208b48654019c2d24e90 -size 795 +oid sha256:b975bbbcb1573af4c2af4dac991e4b474b15d0eb7e568e24ea68928982e3100a +size 429 diff --git a/assets/voxygen/element/icons/staff_m2.png b/assets/voxygen/element/icons/staff_m2.png index f22841c798..8a62997419 100644 --- a/assets/voxygen/element/icons/staff_m2.png +++ b/assets/voxygen/element/icons/staff_m2.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:813f0d30ac437525faa2adb022ce4f57d1beac2ec60d0e9b671c16111eef379c -size 1091 +oid sha256:393fe7cbf2e6626ff83074120b6a89d9ddfdd2435c13c79a493789a41c1403f4 +size 462 diff --git a/assets/voxygen/element/misc_bg/crafting.png b/assets/voxygen/element/misc_bg/crafting.png new file mode 100644 index 0000000000..bd6cc4ac0d --- /dev/null +++ b/assets/voxygen/element/misc_bg/crafting.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1800710a64a8bb5be612f4cc6c88acd2c560d9125cf69899b5f35bf9e137aa42 +size 7737 diff --git a/assets/voxygen/element/misc_bg/crafting_frame.png b/assets/voxygen/element/misc_bg/crafting_frame.png new file mode 100644 index 0000000000..602b96bc20 --- /dev/null +++ b/assets/voxygen/element/misc_bg/crafting_frame.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cfa17b2872fc21a8974e3955c14efda8017e87a8de9f72f42b9d5207eeaa1053 +size 15174 diff --git a/assets/voxygen/element/misc_bg/inv_bg.png b/assets/voxygen/element/misc_bg/inv_bg.png index 0c2c9ccf9a..54ad64bfcf 100644 --- a/assets/voxygen/element/misc_bg/inv_bg.png +++ b/assets/voxygen/element/misc_bg/inv_bg.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:68aa80432b2ec6ff15179a5092b976b8938aa0fa2dba5248536b90ba4838ecd6 -size 58872 +oid sha256:96af9310d82d646b8a1b109e433069ee753d6cf7f5dfd24a2979ecc711fb0048 +size 53053 diff --git a/assets/voxygen/element/misc_bg/map_bg.png b/assets/voxygen/element/misc_bg/map_bg.png index 5afd1cb248..fba5549934 100644 --- a/assets/voxygen/element/misc_bg/map_bg.png +++ b/assets/voxygen/element/misc_bg/map_bg.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a9e597d8bc0327e5802d68eed05b68953e5c1511ee7698a5091fa31ead38a0e5 -size 5494 +oid sha256:0f1c93ac6021ddd3509fb7b0303cb3e1936a6486bcf3c9de322ee44e0e029339 +size 5070 diff --git a/assets/voxygen/element/misc_bg/window.png b/assets/voxygen/element/misc_bg/window.png new file mode 100644 index 0000000000..a3f9d7fca5 --- /dev/null +++ b/assets/voxygen/element/misc_bg/window.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4aeb912ea4143418b5d90026d094733f442241b6a1e80a9417e5c969778adb3d +size 6415 diff --git a/assets/voxygen/i18n/de_DE.ron b/assets/voxygen/i18n/de_DE.ron index 32e371ca31..b669edc521 100644 --- a/assets/voxygen/i18n/de_DE.ron +++ b/assets/voxygen/i18n/de_DE.ron @@ -304,6 +304,8 @@ magischen Gegenstände ergattern?"#, "hud.settings.audio_device": "Ausgabegerät", "hud.settings.awaitingkey": "Drückt eine Taste...", + "hud.settings.unbound": "-", + "hud.settings.reset_keybinds": "Auf Standard zurücksetzen", "hud.social": "Sozial", "hud.social.online": "Online", @@ -314,6 +316,12 @@ magischen Gegenstände ergattern?"#, "hud.spell": "Zauber", + "hud.crafting": "Herstellen", + "hud.crafting.recipes": "Rezepte", + "hud.crafting.ingredients": "Zutaten:", + "hud.crafting.craft": "Herstellen", + "hud.crafting.tool_cata": "Benötigt:", + "hud.free_look_indicator": "Freie Sicht aktiv", "hud.auto_walk_indicator": "Automatisches Laufen aktiv", diff --git a/assets/voxygen/i18n/en.ron b/assets/voxygen/i18n/en.ron index 91073a0a08..d727a07c90 100644 --- a/assets/voxygen/i18n/en.ron +++ b/assets/voxygen/i18n/en.ron @@ -118,8 +118,6 @@ Thanks for taking the time to read this notice, we hope you enjoy the game! // Login process description "main.login_process": r#"Information on the Login Process: -If you are having issues signing in: - Please note that you now need an account to play on auth-enabled servers. @@ -313,7 +311,13 @@ magically infused items?"#, "hud.social.faction": "Faction", "hud.social.play_online_fmt": "{nb_player} player(s) online", - "hud.spell": "Spells", + "hud.crafting": "Crafting", + "hud.crafting.recipes": "Recipes", + "hud.crafting.ingredients": "Ingredients:", + "hud.crafting.craft": "Craft", + "hud.crafting.tool_cata": "Requires:", + + "hud.spell": "Spells", "hud.free_look_indicator": "Free look active", "hud.auto_walk_indicator": "Auto walk active", diff --git a/assets/voxygen/item_image_manifest.ron b/assets/voxygen/item_image_manifest.ron index 68c68ffc4f..af27a24813 100644 --- a/assets/voxygen/item_image_manifest.ron +++ b/assets/voxygen/item_image_manifest.ron @@ -191,7 +191,7 @@ Tool(Hammer(BasicHammer)): VoxTrans( "voxel.weapon.hammer.rusty_2h", (2.0, -1.0, 0.0), (-135.0, 90.0, 0.0), 1.1, - ), + ), // Staffs Tool(Staff(BasicStaff)): VoxTrans( "voxel.weapon.staff.wood-fire", @@ -743,24 +743,30 @@ ), // Consumables Consumable(Apple): - VoxTrans( + Png( "element.icons.item_apple", - (0.0, 0.0, 0.0), (-90.0, 90.0, 0.0), 1.0, ), Consumable(Coconut): Png( "element.icons.item_coconut", ), + Consumable(PotionMed): VoxTrans( + "voxel.object.potion_red", + (0.0, 0.0, 0.0), (-50.0, 30.0, 20.0), 0.7, + ), Consumable(PotionMinor): VoxTrans( "voxel.object.potion_red", - (0.0, 0.0, 0.0), (-50.0, 30.0, 20.0), 0.8, + (0.0, 0.0, 0.0), (-50.0, 30.0, 20.0), 0.5, + ), + Consumable(PotionLarge): VoxTrans( + "voxel.object.potion_red", + (0.0, 0.0, 0.0), (-50.0, 30.0, 20.0), 0.9, ), Consumable(PotionExp): VoxTrans( "voxel.object.potion_turq", (0.0, 0.0, 0.0), (-50.0, 30.0, 20.0), 0.8, ), - Consumable(Cheese): VoxTrans( + Consumable(Cheese): Png( "element.icons.item_cheese", - (0.0, 0.0, 0.0), (-90.0, 90.0, 0.0), 0.9, ), Consumable(Potion): VoxTrans( "voxel.object.potion_red", @@ -776,18 +782,32 @@ ), Consumable(VeloriteFrag): VoxTrans( "voxel.sprite.velorite.velorite_1", - (0.0, -1.0, 0.0), (-50.0, 40.0, 20.0), 0.8, + (0.0, 0.0, 0.0), (-50.0, 40.0, 20.0), 0.8, ), + Consumable(AppleShroomCurry): Png( + "element.icons.item_apple_curry", + ), + Consumable(AppleStick): Png( + "element.icons.item_apple_stick", + ), + Consumable(MushroomStick): Png( + "element.icons.item_shroom_stick", + ), + // Throwables Throwable(Bomb): VoxTrans( "voxel.object.bomb", - (0.0, -1.0, 0.0), (-50.0, 40.0, 20.0), 0.8, + (0.0, 0.5, 0.0), (-50.0, 40.0, 20.0), 0.8, ), Throwable(TrainingDummy): VoxTrans( "voxel.object.training_dummy", (0.0, -1.0, 0.0), (-50.0, 40.0, 20.0), 0.8, ), // Ingredients + Ingredient(CraftsmanHammer): VoxTrans( //TODO This should be a 1h hammer! + "voxel.weapon.hammer.craftsman", + (1.0, 1.0, 0.0), (-135.0, 90.0, 0.0), 1.0, + ), Ingredient(Flower): VoxTrans( "voxel.sprite.flowers.flower_red_2", (0.0, -1.0, 0.0), (-50.0, 40.0, 20.0), 0.8, @@ -796,6 +816,27 @@ "voxel.sprite.grass.grass_long_5", (0.0, 0.0, 0.0), (-90.0, 50.0, 0.0), 1.0, ), + Ingredient(Stones): VoxTrans( + "voxel.sprite.rocks.rock-0", + (0.0, 0.0, 0.0), (-50.0, 40.0, 20.0), 0.8, + ), + Ingredient(Twigs): VoxTrans( + "voxel.sprite.twigs.twigs-0", + (0.0, 0.0, 0.0), (-20.0, 10.0, 20.0), 0.9, + ), + Ingredient(LeatherScraps): Png( + "element.icons.item_leather0", + ), + Ingredient(ShinyGem): Png( + "element.icons.gem", + ), + Ingredient(MortarPestle): Png( + "element.icons.item_mortarpestlecoco", + ), + Ingredient(EmptyVial): VoxTrans( + "voxel.object.potion_empty", + (0.0, 0.0, 0.0), (-50.0, 30.0, 20.0), 0.8, + ), // Debug Items Tool(Debug(Boost)): VoxTrans( "voxel.weapon.tool.broom_belzeshrub_purple", diff --git a/assets/voxygen/voxel/humanoid_main_weapon_manifest.ron b/assets/voxygen/voxel/humanoid_main_weapon_manifest.ron index 3ca33ada52..3efea95da9 100644 --- a/assets/voxygen/voxel/humanoid_main_weapon_manifest.ron +++ b/assets/voxygen/voxel/humanoid_main_weapon_manifest.ron @@ -176,6 +176,10 @@ vox_spec: ("weapon.hammer.rusty_2h", (-2.5, -5.5, -4.0)), color: None ), + /*Dagger(Craftsman): ( //TODO This should be a 1h hammer! + vox_spec: ("weapon.hammer.craftsman", (-2.0, -5.0, -5.5)), + color: None + ),*/ // Daggers Dagger(BasicDagger): ( vox_spec: ("weapon.dagger.dagger_rusty", (-1.5, -3.0, -3.0)), diff --git a/assets/voxygen/voxel/object/potion_empty.vox b/assets/voxygen/voxel/object/potion_empty.vox new file mode 100644 index 0000000000..4f4bd99856 --- /dev/null +++ b/assets/voxygen/voxel/object/potion_empty.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0e7d2dc44a3bd0035ea3ef934cd9420360dd118ada159ac18a187fc0308a246f +size 1400 diff --git a/assets/voxygen/voxel/sprite/gem/gem_blue.vox b/assets/voxygen/voxel/sprite/gem/gem_blue.vox new file mode 100644 index 0000000000..e8ab838a40 --- /dev/null +++ b/assets/voxygen/voxel/sprite/gem/gem_blue.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5b00fd0d2018c4923134e675e0ccb5db8e0f0f8da5387a7fca4cf299b07a300e +size 1184 diff --git a/assets/voxygen/voxel/sprite/gem/gem_green.vox b/assets/voxygen/voxel/sprite/gem/gem_green.vox new file mode 100644 index 0000000000..285d6170eb --- /dev/null +++ b/assets/voxygen/voxel/sprite/gem/gem_green.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7238b7eb2d8a2ba98afdf3cfd7eeadf5a5832a6fc0f285e00008487553309f91 +size 1208 diff --git a/assets/voxygen/voxel/sprite/gem/gem_red.vox b/assets/voxygen/voxel/sprite/gem/gem_red.vox new file mode 100644 index 0000000000..6765230e7b --- /dev/null +++ b/assets/voxygen/voxel/sprite/gem/gem_red.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:61c11c616e3ed4d011ead74acf1273823936a85deae7b3db214a2aa6ab54ee72 +size 1336 diff --git a/assets/voxygen/voxel/sprite/rocks/rock-0.vox b/assets/voxygen/voxel/sprite/rocks/rock-0.vox new file mode 100644 index 0000000000..bd0c85c9be --- /dev/null +++ b/assets/voxygen/voxel/sprite/rocks/rock-0.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:219f3cfd7e310284e17397a59441cfc79d27281ca09bea8a4740eee4173dab89 +size 1336 diff --git a/assets/voxygen/voxel/sprite/rocks/rock-1.vox b/assets/voxygen/voxel/sprite/rocks/rock-1.vox new file mode 100644 index 0000000000..ca13137003 --- /dev/null +++ b/assets/voxygen/voxel/sprite/rocks/rock-1.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2c9365b3ddee1c873a9dbbe0a9e5c86280561562e14cda6540235d9d58bc58d0 +size 1936 diff --git a/assets/voxygen/voxel/sprite/rocks/rock-2.vox b/assets/voxygen/voxel/sprite/rocks/rock-2.vox new file mode 100644 index 0000000000..42466dd089 --- /dev/null +++ b/assets/voxygen/voxel/sprite/rocks/rock-2.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:64e5992e0833d6f22ffb833e83ee967b1edb55d52414d45ce3afada7b0fc8e82 +size 1368 diff --git a/assets/voxygen/voxel/sprite/twigs/twigs-0.vox b/assets/voxygen/voxel/sprite/twigs/twigs-0.vox new file mode 100644 index 0000000000..e91efb6c3b --- /dev/null +++ b/assets/voxygen/voxel/sprite/twigs/twigs-0.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fd6c3dedba6acda92de5d70400ec8fe886f73015f3a2f088c7907ecbf57a6319 +size 1144 diff --git a/assets/voxygen/voxel/sprite/twigs/twigs-1.vox b/assets/voxygen/voxel/sprite/twigs/twigs-1.vox new file mode 100644 index 0000000000..4584d748e2 --- /dev/null +++ b/assets/voxygen/voxel/sprite/twigs/twigs-1.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e7829f29cd6bc68c9d9f5a04ab7c9feefb73031f438b777ddd81d1f67415c7f8 +size 1120 diff --git a/assets/voxygen/voxel/sprite/twigs/twigs-2.vox b/assets/voxygen/voxel/sprite/twigs/twigs-2.vox new file mode 100644 index 0000000000..5647afff1f --- /dev/null +++ b/assets/voxygen/voxel/sprite/twigs/twigs-2.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:77a7ead81babcbbd6c5954df0692a7e13e018a250f379f42a755914546ad189c +size 1184 diff --git a/assets/voxygen/voxel/weapon/hammer/craftsman.vox b/assets/voxygen/voxel/weapon/hammer/craftsman.vox new file mode 100644 index 0000000000..7501e83d4f --- /dev/null +++ b/assets/voxygen/voxel/weapon/hammer/craftsman.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d4dccf2543585a5d0a18dea671d9b25347715a89ccab44e793faf735a0d52e62 +size 2240 diff --git a/client/src/lib.rs b/client/src/lib.rs index 9e939c91c8..482739defe 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -1,5 +1,5 @@ #![deny(unsafe_code)] -#![feature(label_break_value)] +#![feature(label_break_value, option_zip)] pub mod cmd; pub mod error; @@ -25,6 +25,7 @@ use common::{ PlayerInfo, PlayerListUpdate, RegisterError, RequestStateError, ServerInfo, ServerMsg, MAX_BYTES_CHAT_MSG, }, + recipe::RecipeBook, state::State, sync::{Uid, UidAllocator, WorldSyncExt}, terrain::{block::Block, TerrainChunk, TerrainChunkSize}, @@ -33,7 +34,7 @@ use common::{ use futures_executor::block_on; use futures_timer::Delay; use futures_util::{select, FutureExt}; -use hashbrown::HashMap; +use hashbrown::{HashMap, HashSet}; use image::DynamicImage; use network::{ Network, Participant, Pid, ProtocolAddr, Stream, PROMISES_CONSISTENCY, PROMISES_ORDERED, @@ -75,6 +76,8 @@ pub struct Client { pub player_list: HashMap, pub character_list: CharacterList, pub active_character_id: Option, + recipe_book: RecipeBook, + available_recipes: HashSet, _network: Network, participant: Option, @@ -123,7 +126,7 @@ impl Client { let mut stream = block_on(participant.open(10, PROMISES_ORDERED | PROMISES_CONSISTENCY))?; // Wait for initial sync - let (state, entity, server_info, world_map) = block_on(async { + let (state, entity, server_info, world_map, recipe_book) = block_on(async { loop { match stream.recv().await? { ServerMsg::InitialSync { @@ -131,6 +134,7 @@ impl Client { server_info, time_of_day, world_map: (map_size, world_map), + recipe_book, } => { // TODO: Display that versions don't match in Voxygen if &server_info.git_hash != *common::util::GIT_HASH { @@ -174,7 +178,13 @@ impl Client { ); debug!("Done preparing image..."); - break Ok((state, entity, server_info, (world_map, map_size))); + break Ok(( + state, + entity, + server_info, + (world_map, map_size), + recipe_book, + )); }, ServerMsg::TooManyPlayers => break Err(Error::TooManyPlayers), err => { @@ -200,6 +210,8 @@ impl Client { player_list: HashMap::new(), character_list: CharacterList::default(), active_character_id: None, + recipe_book, + available_recipes: HashSet::default(), _network: network, participant: Some(participant), @@ -372,6 +384,40 @@ impl Client { } } + pub fn recipe_book(&self) -> &RecipeBook { &self.recipe_book } + + pub fn available_recipes(&self) -> &HashSet { &self.available_recipes } + + pub fn can_craft_recipe(&self, recipe: &str) -> bool { + self.recipe_book + .get(recipe) + .zip(self.inventories().get(self.entity)) + .map(|(recipe, inv)| inv.contains_ingredients(&*recipe).is_ok()) + .unwrap_or(false) + } + + pub fn craft_recipe(&mut self, recipe: &str) -> bool { + if self.can_craft_recipe(recipe) { + self.singleton_stream + .send(ClientMsg::ControlEvent(ControlEvent::InventoryManip( + InventoryManip::CraftRecipe(recipe.to_string()), + ))) + .unwrap(); + true + } else { + false + } + } + + fn update_available_recipes(&mut self) { + self.available_recipes = self + .recipe_book + .iter() + .map(|(name, _)| name.clone()) + .filter(|name| self.can_craft_recipe(name)) + .collect(); + } + pub fn toggle_lantern(&mut self) { self.singleton_stream .send(ClientMsg::ControlEvent(ControlEvent::ToggleLantern)) @@ -949,6 +995,8 @@ impl Client { }, } + self.update_available_recipes(); + frontend_events.push(Event::InventoryUpdated(event)); }, ServerMsg::TerrainChunkUpdate { key, chunk } => { diff --git a/common/src/assets/mod.rs b/common/src/assets/mod.rs index e3737728b6..f23ba05632 100644 --- a/common/src/assets/mod.rs +++ b/common/src/assets/mod.rs @@ -80,13 +80,14 @@ pub fn load_map A>( specifier: &str, f: F, ) -> Result, Error> { - let mut assets_write = ASSETS.write().unwrap(); + let assets_write = ASSETS.read().unwrap(); match assets_write.get(specifier) { Some(asset) => Ok(Arc::clone(asset).downcast()?), None => { + drop(assets_write); // Drop the asset hashmap to permit recursive loading let asset = Arc::new(f(A::parse(load_file(specifier, A::ENDINGS)?)?)); let clone = Arc::clone(&asset); - assets_write.insert(specifier.to_owned(), clone); + ASSETS.write().unwrap().insert(specifier.to_owned(), clone); Ok(asset) }, } diff --git a/common/src/comp/controller.rs b/common/src/comp/controller.rs index ca8bcda1b0..01942d68f1 100644 --- a/common/src/comp/controller.rs +++ b/common/src/comp/controller.rs @@ -15,6 +15,7 @@ pub enum InventoryManip { Use(Slot), Swap(Slot, Slot), Drop(Slot), + CraftRecipe(String), } #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] diff --git a/common/src/comp/inventory/item/armor.rs b/common/src/comp/inventory/item/armor.rs index 3715c88b0b..65e1ebe560 100644 --- a/common/src/comp/inventory/item/armor.rs +++ b/common/src/comp/inventory/item/armor.rs @@ -349,5 +349,14 @@ pub enum Armor { Tabard(Tabard), } +impl Armor { + /// Determines whether two pieces of armour are superficially equivalent to + /// one another (i.e: one may be substituted for the other in crafting + /// recipes or item possession checks). + pub fn superficially_eq(&self, other: &Self) -> bool { + std::mem::discriminant(self) == std::mem::discriminant(other) + } +} + #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct Stats(pub u32); diff --git a/common/src/comp/inventory/item/mod.rs b/common/src/comp/inventory/item/mod.rs index 8ebb2ba2b7..a210ae2d8b 100644 --- a/common/src/comp/inventory/item/mod.rs +++ b/common/src/comp/inventory/item/mod.rs @@ -26,7 +26,12 @@ pub enum Consumable { Velorite, VeloriteFrag, PotionMinor, + PotionMed, + PotionLarge, PotionExp, + AppleShroomCurry, + AppleStick, + MushroomStick, } #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] @@ -44,6 +49,13 @@ pub enum Utility { pub enum Ingredient { Flower, Grass, + EmptyVial, + LeatherScraps, + ShinyGem, + Stones, + Twigs, + MortarPestle, + CraftsmanHammer, } #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] @@ -159,13 +171,25 @@ impl Item { pub fn description(&self) -> &str { &self.description } + pub fn amount(&self) -> u32 { + match &self.kind { + ItemKind::Tool(_) => 1, + ItemKind::Lantern(_) => 1, + ItemKind::Armor { .. } => 1, + ItemKind::Consumable { amount, .. } => *amount, + ItemKind::Throwable { amount, .. } => *amount, + ItemKind::Utility { amount, .. } => *amount, + ItemKind::Ingredient { amount, .. } => *amount, + } + } + pub fn try_reclaim_from_block(block: Block) -> Option { match block.kind() { - BlockKind::Apple => Some(assets::load_expect_cloned("common.items.apple")), - BlockKind::Mushroom => Some(assets::load_expect_cloned("common.items.mushroom")), - BlockKind::Velorite => Some(assets::load_expect_cloned("common.items.velorite")), + BlockKind::Apple => Some(assets::load_expect_cloned("common.items.food.apple")), + BlockKind::Mushroom => Some(assets::load_expect_cloned("common.items.food.mushroom")), + BlockKind::Velorite => Some(assets::load_expect_cloned("common.items.ore.velorite")), BlockKind::VeloriteFrag => { - Some(assets::load_expect_cloned("common.items.veloritefrag")) + Some(assets::load_expect_cloned("common.items.ore.veloritefrag")) }, BlockKind::BlueFlower => Some(assets::load_expect_cloned("common.items.flowers.blue")), BlockKind::PinkFlower => Some(assets::load_expect_cloned("common.items.flowers.pink")), @@ -185,16 +209,44 @@ impl Item { Some(assets::load_expect_cloned("common.items.grasses.medium")) }, BlockKind::ShortGrass => Some(assets::load_expect_cloned("common.items.grasses.short")), - BlockKind::Coconut => Some(assets::load_expect_cloned("common.items.coconut")), + BlockKind::Coconut => Some(assets::load_expect_cloned("common.items.food.coconut")), BlockKind::Chest => { let chosen = assets::load_expect::>("common.loot_table"); let chosen = chosen.choose(); Some(assets::load_expect_cloned(chosen)) }, + BlockKind::Stones => Some(assets::load_expect_cloned( + "common.items.crafting_ing.stones", + )), + BlockKind::Twigs => Some(assets::load_expect_cloned( + "common.items.crafting_ing.twigs", + )), + BlockKind::ShinyGem => Some(assets::load_expect_cloned( + "common.items.crafting_ing.shiny_gem", + )), _ => None, } } + + /// Determines whether two items are superficially equivalent to one another + /// (i.e: one may be substituted for the other in crafting recipes or + /// item possession checks). + pub fn superficially_eq(&self, other: &Self) -> bool { + match (&self.kind, &other.kind) { + (ItemKind::Tool(a), ItemKind::Tool(b)) => a.superficially_eq(b), + // TODO: Differentiate between lantern colors? + (ItemKind::Lantern(_), ItemKind::Lantern(_)) => true, + (ItemKind::Armor { kind: a, .. }, ItemKind::Armor { kind: b, .. }) => { + a.superficially_eq(b) + }, + (ItemKind::Consumable { kind: a, .. }, ItemKind::Consumable { kind: b, .. }) => a == b, + (ItemKind::Throwable { kind: a, .. }, ItemKind::Throwable { kind: b, .. }) => a == b, + (ItemKind::Utility { kind: a, .. }, ItemKind::Utility { kind: b, .. }) => a == b, + (ItemKind::Ingredient { kind: a, .. }, ItemKind::Ingredient { kind: b, .. }) => a == b, + _ => false, + } + } } impl Component for Item { diff --git a/common/src/comp/inventory/item/tool.rs b/common/src/comp/inventory/item/tool.rs index abeb4a04c9..4409551306 100644 --- a/common/src/comp/inventory/item/tool.rs +++ b/common/src/comp/inventory/item/tool.rs @@ -453,4 +453,11 @@ impl Tool { }], } } + + /// Determines whether two tools are superficially equivalent to one another + /// (i.e: one may be substituted for the other in crafting recipes or + /// item possession checks). + pub fn superficially_eq(&self, other: &Self) -> bool { + ToolCategory::from(self.kind) == ToolCategory::from(other.kind) + } } diff --git a/common/src/comp/inventory/mod.rs b/common/src/comp/inventory/mod.rs index 657386fec5..22b3622971 100644 --- a/common/src/comp/inventory/mod.rs +++ b/common/src/comp/inventory/mod.rs @@ -1,7 +1,7 @@ pub mod item; pub mod slot; -use crate::assets; +use crate::{assets, recipe::Recipe}; use item::{Consumable, Item, ItemKind}; use serde::{Deserialize, Serialize}; use specs::{Component, FlaggedStorage, HashMapStorage}; @@ -185,14 +185,11 @@ impl Inventory { /// Add a series of items to inventory, returning any which do not fit as an /// error. - pub fn push_all>(&mut self, mut items: I) -> Result<(), Error> { + pub fn push_all>(&mut self, items: I) -> Result<(), Error> { // Vec doesn't allocate for zero elements so this should be cheap let mut leftovers = Vec::new(); - let mut slots = self.slots.iter_mut(); - for item in &mut items { - if let Some(slot) = slots.find(|slot| slot.is_none()) { - slot.replace(item); - } else { + for item in items { + if let Some(item) = self.push(item) { leftovers.push(item); } } @@ -340,6 +337,52 @@ impl Inventory { None } } + + /// Determine how many of a particular item there is in the inventory. + pub fn item_count(&self, item: &Item) -> usize { + self.slots() + .iter() + .flatten() + .filter(|it| it.superficially_eq(item)) + .map(|it| it.amount() as usize) + .sum() + } + + /// Determine whether the inventory contains the ingredients for a recipe. + /// If it does, return a vector of numbers, where is number corresponds + /// to an inventory slot, along with the number of items that need + /// removing from it. It items are missing, return the missing items, and + /// how many are missing. + pub fn contains_ingredients<'a>( + &self, + recipe: &'a Recipe, + ) -> Result, Vec<(&'a Item, usize)>> { + let mut slot_claims = vec![0; self.slots.len()]; + let mut missing = Vec::new(); + + for (input, mut needed) in recipe.inputs() { + let mut contains_any = false; + + for (i, slot) in self.slots().iter().enumerate() { + if let Some(item) = slot.as_ref().filter(|item| item.superficially_eq(input)) { + let can_claim = (item.amount() as usize - slot_claims[i]).min(needed); + slot_claims[i] += can_claim; + needed -= can_claim; + contains_any = true; + } + } + + if needed > 0 || !contains_any { + missing.push((input, needed)); + } + } + + if missing.len() == 0 { + Ok(slot_claims) + } else { + Err(missing) + } + } } impl Default for Inventory { @@ -348,8 +391,8 @@ impl Default for Inventory { slots: vec![None; 36], amount: 0, }; - inventory.push(assets::load_expect_cloned("common.items.cheese")); - inventory.push(assets::load_expect_cloned("common.items.apple")); + inventory.push(assets::load_expect_cloned("common.items.food.cheese")); + inventory.push(assets::load_expect_cloned("common.items.food.apple")); inventory } } @@ -371,6 +414,7 @@ pub enum InventoryUpdateEvent { CollectFailed, Possession, Debug, + Craft, } impl Default for InventoryUpdateEvent { diff --git a/common/src/lib.rs b/common/src/lib.rs index c2d04628e3..fb04ff28b1 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -26,6 +26,7 @@ pub mod msg; pub mod npc; pub mod path; pub mod ray; +pub mod recipe; pub mod region; pub mod spiral; pub mod state; diff --git a/common/src/msg/server.rs b/common/src/msg/server.rs index a766a7ef37..a83dc6216e 100644 --- a/common/src/msg/server.rs +++ b/common/src/msg/server.rs @@ -1,7 +1,9 @@ use super::{ClientState, EcsCompPacket}; use crate::{ character::CharacterItem, - comp, state, sync, + comp, + recipe::RecipeBook, + state, sync, sync::Uid, terrain::{Block, TerrainChunk}, }; @@ -58,6 +60,7 @@ pub enum ServerMsg { server_info: ServerInfo, time_of_day: state::TimeOfDay, world_map: (Vec2, Vec), + recipe_book: RecipeBook, }, /// An error occurred while loading character data CharacterDataLoadError(String), diff --git a/common/src/recipe.rs b/common/src/recipe.rs new file mode 100644 index 0000000000..bad5a81abf --- /dev/null +++ b/common/src/recipe.rs @@ -0,0 +1,99 @@ +use crate::{ + assets::{self, Asset}, + comp::{Inventory, Item}, +}; +use hashbrown::HashMap; +use serde::{Deserialize, Serialize}; +use std::{fs::File, io::BufReader, sync::Arc}; + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct Recipe { + pub output: (Item, usize), + pub inputs: Vec<(Item, usize)>, +} + +#[allow(clippy::type_complexity)] +impl Recipe { + /// Perform a recipe, returning a list of missing items on failure + pub fn perform( + &self, + inv: &mut Inventory, + ) -> Result, Vec<(&Item, usize)>> { + // Get ingredient cells from inventory, + inv.contains_ingredients(self)? + .into_iter() + .enumerate() + .for_each(|(i, n)| { + (0..n).for_each(|_| { + inv.take(i).expect("Expected item to exist in inventory"); + }) + }); + + for i in 0..self.output.1 { + if let Some(item) = inv.push(self.output.0.clone()) { + return Ok(Some((item, self.output.1 - i))); + } + } + + Ok(None) + } + + pub fn inputs(&self) -> impl ExactSizeIterator { + self.inputs.iter().map(|(item, amount)| (item, *amount)) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct RecipeBook { + recipes: HashMap, +} + +impl RecipeBook { + pub fn get(&self, recipe: &str) -> Option<&Recipe> { self.recipes.get(recipe) } + + pub fn iter(&self) -> impl ExactSizeIterator { self.recipes.iter() } + + pub fn get_available(&self, inv: &Inventory) -> Vec<(String, Recipe)> { + self.recipes + .iter() + .filter(|(_, recipe)| inv.contains_ingredients(recipe).is_ok()) + .map(|(name, recipe)| (name.clone(), recipe.clone())) + .collect() + } +} + +impl Asset for RecipeBook { + const ENDINGS: &'static [&'static str] = &["ron"]; + + fn parse(buf_reader: BufReader) -> Result { + ron::de::from_reader::< + BufReader, + HashMap)>, + >(buf_reader) + .map_err(assets::Error::parse_error) + .and_then(|recipes| { + Ok(RecipeBook { + recipes: recipes + .into_iter() + .map::, _>( + |(name, ((output, amount), inputs))| { + Ok((name, Recipe { + output: ((&*assets::load::(&output)?).clone(), amount), + inputs: inputs + .into_iter() + .map::, _>( + |(name, amount)| { + Ok(((&*assets::load::(&name)?).clone(), amount)) + }, + ) + .collect::>()?, + })) + }, + ) + .collect::>()?, + }) + }) + } +} + +pub fn default_recipe_book() -> Arc { assets::load_expect("common.recipe_book") } diff --git a/common/src/terrain/block.rs b/common/src/terrain/block.rs index 6c69a9ee35..bf6886c0ca 100644 --- a/common/src/terrain/block.rs +++ b/common/src/terrain/block.rs @@ -83,6 +83,9 @@ pub enum BlockKind { WardrobeDouble, LargeGrass, Pot, + Stones, + Twigs, + ShinyGem, } impl BlockKind { @@ -167,6 +170,9 @@ impl BlockKind { BlockKind::WardrobeSingle => false, BlockKind::WardrobeDouble => false, BlockKind::Pot => false, + BlockKind::Stones => true, + BlockKind::Twigs => true, + BlockKind::ShinyGem => true, _ => false, } } @@ -252,6 +258,9 @@ impl BlockKind { BlockKind::WardrobeDouble => false, BlockKind::LargeGrass => false, BlockKind::Pot => false, + BlockKind::Stones => false, + BlockKind::Twigs => false, + BlockKind::ShinyGem => false, _ => true, } } @@ -323,6 +332,9 @@ impl BlockKind { BlockKind::WardrobeSingle => true, BlockKind::WardrobeDouble => true, BlockKind::Pot => true, + BlockKind::Stones => false, + BlockKind::Twigs => false, + BlockKind::ShinyGem => false, _ => true, } } @@ -389,6 +401,9 @@ impl BlockKind { BlockKind::VeloriteFrag => true, BlockKind::Chest => true, BlockKind::Coconut => true, + BlockKind::Stones => true, + BlockKind::Twigs => true, + BlockKind::ShinyGem => true, _ => false, } } diff --git a/server/src/events/inventory_manip.rs b/server/src/events/inventory_manip.rs index b33a170e0b..7c1d4ee71d 100644 --- a/server/src/events/inventory_manip.rs +++ b/server/src/events/inventory_manip.rs @@ -5,6 +5,7 @@ use common::{ slot::{self, Slot}, Pos, MAX_PICKUP_RANGE_SQR, }, + recipe::default_recipe_book, sync::{Uid, WorldSyncExt}, terrain::block::Block, vol::{ReadVol, Vox}, @@ -165,16 +166,10 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv thrown_items.push(( *pos, state - .ecs() - .read_storage::() - .get(entity) - .copied() + .read_component_cloned::(entity) .unwrap_or_default(), state - .ecs() - .read_storage::() - .get(entity) - .copied() + .read_component_cloned::(entity) .unwrap_or_default(), *kind, )); @@ -317,10 +312,7 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv dropped_items.push(( *pos, state - .ecs() - .read_storage::() - .get(entity) - .copied() + .read_component_cloned::(entity) .unwrap_or_default(), item, )); @@ -330,6 +322,39 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv comp::InventoryUpdate::new(comp::InventoryUpdateEvent::Dropped), ); }, + + comp::InventoryManip::CraftRecipe(recipe) => { + if let Some(inv) = state + .ecs() + .write_storage::() + .get_mut(entity) + { + let recipe_book = default_recipe_book(); + let craft_result = recipe_book.get(&recipe).and_then(|r| r.perform(inv).ok()); + + if craft_result.is_some() { + let _ = state.ecs().write_storage().insert( + entity, + comp::InventoryUpdate::new(comp::InventoryUpdateEvent::Craft), + ); + } + + // Drop the item if there wasn't enough space + if let Some(Some((item, amount))) = craft_result { + for _ in 0..amount { + dropped_items.push(( + state + .read_component_cloned::(entity) + .unwrap_or_default(), + state + .read_component_cloned::(entity) + .unwrap_or_default(), + item.clone(), + )); + } + } + } + }, } // Drop items diff --git a/server/src/lib.rs b/server/src/lib.rs index 11d7d95093..88166f25e9 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -32,6 +32,7 @@ use common::{ comp::{self, ChatType}, event::{EventBus, ServerEvent}, msg::{ClientState, ServerInfo, ServerMsg}, + recipe::default_recipe_book, state::{State, TimeOfDay}, sync::WorldSyncExt, terrain::TerrainChunkSize, @@ -636,6 +637,7 @@ impl Server { server_info: self.get_server_info(), time_of_day: *self.state.ecs().read_resource(), world_map: (WORLD_SIZE.map(|e| e as u32), self.map.clone()), + recipe_book: (&*default_recipe_book()).clone(), }); frontend_events.push(Event::ClientConnected { entity }); diff --git a/voxygen/src/hud/bag.rs b/voxygen/src/hud/bag.rs index 7e38e47cf5..65f6e34d6d 100644 --- a/voxygen/src/hud/bag.rs +++ b/voxygen/src/hud/bag.rs @@ -30,7 +30,6 @@ widget_ids! { inv_grid_2, inv_scrollbar, inv_slots_0, - map_title, inv_slots[], //tooltip[], bg, diff --git a/voxygen/src/hud/buttons.rs b/voxygen/src/hud/buttons.rs index 167aa0f117..e1925a065e 100644 --- a/voxygen/src/hud/buttons.rs +++ b/voxygen/src/hud/buttons.rs @@ -37,6 +37,11 @@ widget_ids! { spellbook_button_bg, spellbook_text, spellbook_text_bg, + crafting_button, + crafting_button_bg, + crafting_text, + crafting_text_bg, + } } const TOOLTIP_UPSHIFT: f64 = 40.0; @@ -93,6 +98,7 @@ pub enum Event { ToggleMap, ToggleSocial, ToggleSpell, + ToggleCrafting, } impl<'a> Widget for Buttons<'a> { @@ -360,6 +366,43 @@ impl<'a> Widget for Buttons<'a> { .color(TEXT_COLOR) .set(state.ids.spellbook_text, ui); } + // Crafting + if Button::image(self.imgs.crafting_icon) + .w_h(25.0, 25.0) + .left_from(state.ids.spellbook_button, 10.0) + .hover_image(self.imgs.crafting_icon_hover) + .press_image(self.imgs.crafting_icon_press) + .with_tooltip( + self.tooltip_manager, + &localized_strings.get("hud.crafting"), + "", + &button_tooltip, + ) + .bottom_offset(TOOLTIP_UPSHIFT) + .set(state.ids.crafting_button, ui) + .was_clicked() + { + return Some(Event::ToggleCrafting); + } + if let Some(crafting) = &self + .global_state + .settings + .controls + .get_binding(GameInput::Crafting) + { + Text::new(crafting.to_string().as_str()) + .bottom_right_with_margins_on(state.ids.crafting_button, 0.0, 0.0) + .font_size(10) + .font_id(self.fonts.cyri.conrod_id) + .color(BLACK) + .set(state.ids.crafting_text_bg, ui); + Text::new(crafting.to_string().as_str()) + .bottom_right_with_margins_on(state.ids.crafting_text_bg, 1.0, 1.0) + .font_size(10) + .font_id(self.fonts.cyri.conrod_id) + .color(TEXT_COLOR) + .set(state.ids.crafting_text, ui); + } None } } diff --git a/voxygen/src/hud/crafting.rs b/voxygen/src/hud/crafting.rs new file mode 100644 index 0000000000..19fff3dfa6 --- /dev/null +++ b/voxygen/src/hud/crafting.rs @@ -0,0 +1,505 @@ +use super::{ + img_ids::{Imgs, ImgsRot}, + item_imgs::ItemImgs, + TEXT_COLOR, TEXT_DULL_RED_COLOR, TEXT_GRAY_COLOR, UI_HIGHLIGHT_0, UI_MAIN, +}; +use crate::{ + i18n::VoxygenLocalization, + ui::{fonts::ConrodVoxygenFonts, ImageFrame, Tooltip, TooltipManager, Tooltipable}, +}; +use client::{self, Client}; +use common::comp::Inventory; +use conrod_core::{ + color, + widget::{self, Button, Image, Rectangle, Scrollbar, Text}, + widget_ids, Color, Colorable, Labelable, Positionable, Sizeable, Widget, WidgetCommon, +}; + +widget_ids! { + pub struct Ids { + window, + window_frame, + close, + icon, + title_main, + title_rec, + align_rec, + scrollbar_rec, + title_ing, + align_ing, + scrollbar_ing, + btn_craft, + recipe_names[], + recipe_img_frame[], + recipe_img[], + ingredients[], + ingredient_frame[], + ingredient_img[], + req_text[], + ingredients_txt, + output_img_frame, + output_img, + } +} + +pub enum Event { + CraftRecipe(String), + Close, +} + +#[derive(WidgetCommon)] +pub struct Crafting<'a> { + client: &'a Client, + imgs: &'a Imgs, + fonts: &'a ConrodVoxygenFonts, + localized_strings: &'a std::sync::Arc, + rot_imgs: &'a ImgsRot, + tooltip_manager: &'a mut TooltipManager, + item_imgs: &'a ItemImgs, + inventory: &'a Inventory, + #[conrod(common_builder)] + common: widget::CommonBuilder, +} +#[allow(clippy::too_many_arguments)] +impl<'a> Crafting<'a> { + pub fn new( + client: &'a Client, + imgs: &'a Imgs, + fonts: &'a ConrodVoxygenFonts, + localized_strings: &'a std::sync::Arc, + rot_imgs: &'a ImgsRot, + tooltip_manager: &'a mut TooltipManager, + item_imgs: &'a ItemImgs, + inventory: &'a Inventory, + ) -> Self { + Self { + client, + imgs, + fonts, + localized_strings, + rot_imgs, + tooltip_manager, + item_imgs, + inventory, + common: widget::CommonBuilder::default(), + } + } +} + +pub struct State { + ids: Ids, + selected_recipe: Option, +} + +impl<'a> Widget for Crafting<'a> { + type Event = Vec; + type State = State; + type Style = (); + + fn init_state(&self, id_gen: widget::id::Generator) -> Self::State { + State { + ids: Ids::new(id_gen), + selected_recipe: None, + } + } + + #[allow(clippy::unused_unit)] // TODO: Pending review in #587 + fn style(&self) -> Self::Style { () } + + fn update(self, args: widget::UpdateArgs) -> Self::Event { + let widget::UpdateArgs { state, ui, .. } = args; + + if state.ids.recipe_names.len() < self.client.recipe_book().iter().len() { + state.update(|state| { + state.ids.recipe_names.resize( + self.client.recipe_book().iter().len(), + &mut ui.widget_id_generator(), + ) + }); + } + /*if state.ids.recipe_img_frame.len() < self.client.recipe_book().iter().len() { + state.update(|state| { + state.ids.recipe_img_frame.resize( + self.client.recipe_book().iter().len(), + &mut ui.widget_id_generator(), + ) + }); + } + if state.ids.recipe_img.len() < self.client.recipe_book().iter().len() { + state.update(|state| { + state.ids.recipe_img.resize( + self.client.recipe_book().iter().len(), + &mut ui.widget_id_generator(), + ) + }); + }*/ + + let ids = &state.ids; + + let mut events = Vec::new(); + + // Tooltips + let item_tooltip = Tooltip::new({ + let edge = &self.rot_imgs.tt_side; + let corner = &self.rot_imgs.tt_corner; + ImageFrame::new( + [edge.cw180, edge.none, edge.cw270, edge.cw90], + [corner.none, corner.cw270, corner.cw90, corner.cw180], + Color::Rgba(0.08, 0.07, 0.04, 1.0), + 5.0, + ) + }) + .title_font_size(self.fonts.cyri.scale(15)) + .parent(ui.window) + .desc_font_size(self.fonts.cyri.scale(12)) + .title_text_color(TEXT_COLOR) + .font_id(self.fonts.cyri.conrod_id) + .desc_text_color(TEXT_COLOR); + + Image::new(self.imgs.crafting_window) + .bottom_right_with_margins_on(ui.window, 308.0, 450.0) + .color(Some(UI_MAIN)) + .w_h(422.0, 460.0) + .set(ids.window, ui); + Image::new(self.imgs.crafting_frame) + .middle_of(ids.window) + .color(Some(UI_HIGHLIGHT_0)) + .w_h(422.0, 460.0) + .set(ids.window_frame, ui); + Image::new(self.imgs.crafting_icon_bordered) + .w_h(38.0, 38.0) + .top_left_with_margins_on(state.ids.window_frame, 4.0, 4.0) + .set(state.ids.icon, ui); + // Close Button + if Button::image(self.imgs.close_button) + .w_h(24.0, 25.0) + .hover_image(self.imgs.close_button_hover) + .press_image(self.imgs.close_button_press) + .top_right_with_margins_on(ids.window, 0.0, 0.0) + .set(ids.close, ui) + .was_clicked() + { + events.push(Event::Close); + } + + // Title + Text::new(&self.localized_strings.get("hud.crafting")) + .mid_top_with_margin_on(ids.window_frame, 9.0) + .font_id(self.fonts.cyri.conrod_id) + .font_size(self.fonts.cyri.scale(22)) + .color(TEXT_COLOR) + .set(ids.title_main, ui); + + // Alignment + Rectangle::fill_with([136.0, 378.0], color::TRANSPARENT) + .top_left_with_margins_on(ids.window_frame, 74.0, 5.0) + .set(ids.align_rec, ui); + Rectangle::fill_with([274.0, 340.0], color::TRANSPARENT) + .top_right_with_margins_on(ids.window, 74.0, 5.0) + .set(ids.align_ing, ui); + let client = &self.client; + // First available recipes, then unavailable ones + let recipe_iter = self + .client + .recipe_book() + .iter() + .filter(|(name, _)| client.available_recipes().contains(name.as_str())) + .map(|(name, recipe)| (name, recipe, true)) + .chain( + client + .recipe_book() + .iter() + .filter(|(name, _)| !client.available_recipes().contains(name.as_str())) + .map(|(name, recipe)| (name, recipe, false)), + ); + match &state.selected_recipe { + None => {}, + Some(recipe) => { + let can_perform = client.available_recipes().contains(recipe.as_str()); + // Ingredients Text + Text::new(&self.localized_strings.get("hud.crafting.ingredients")) + .top_left_with_margins_on(state.ids.align_ing, 10.0, 5.0) + .font_id(self.fonts.cyri.conrod_id) + .font_size(self.fonts.cyri.scale(18)) + .color(TEXT_COLOR) + .set(state.ids.ingredients_txt, ui); + // Craft button + if Button::image(self.imgs.button) + .w_h(105.0, 25.0) + .hover_image( + can_perform + .then_some(self.imgs.button_hover) + .unwrap_or(self.imgs.button), + ) + .press_image( + can_perform + .then_some(self.imgs.button_press) + .unwrap_or(self.imgs.button), + ) + .label(&self.localized_strings.get("hud.crafting.craft")) + .label_y(conrod_core::position::Relative::Scalar(1.0)) + .label_color(can_perform.then_some(TEXT_COLOR).unwrap_or(TEXT_GRAY_COLOR)) + .label_font_size(self.fonts.cyri.scale(12)) + .label_font_id(self.fonts.cyri.conrod_id) + .image_color(can_perform.then_some(TEXT_COLOR).unwrap_or(TEXT_GRAY_COLOR)) + .mid_bottom_with_margin_on(ids.align_ing, -31.0) + .set(ids.btn_craft, ui) + .was_clicked() + { + events.push(Event::CraftRecipe(recipe.clone())); + } + // Result Image BG + Image::new(self.imgs.inv_slot) + .w_h(60.0, 60.0) + .top_right_with_margins_on(state.ids.align_ing, 15.0, 10.0) + .parent(ids.align_ing) + .set(ids.output_img_frame, ui); + + if let Some(recipe) = state + .selected_recipe + .as_ref() + .and_then(|r| self.client.recipe_book().get(r.as_str())) + { + let output_text = format!("x{}", &recipe.output.1.to_string()); + // Output Image + Button::image( + self.item_imgs + .img_id_or_not_found_img((&recipe.output.0).into()), + ) + .w_h(55.0, 55.0) + .middle_of(state.ids.output_img_frame) + .label(if recipe.output.1 > 1 {&output_text} else {""}) // Only show output amount for amounts > 1 + .label_color(TEXT_COLOR) + .label_font_size(self.fonts.cyri.scale(14)) + .label_font_id(self.fonts.cyri.conrod_id) + .label_y(conrod_core::position::Relative::Scalar(-24.0)) + .label_x(conrod_core::position::Relative::Scalar(24.0)) + .with_tooltip( + self.tooltip_manager, + recipe.output.0.name(), + recipe.output.0.description(), + &item_tooltip, + ) + .set(state.ids.output_img, ui); + } + }, + } + + // Recipe list + for (i, (name, recipe, can_perform)) in recipe_iter.enumerate() { + let button = Button::image( + if state + .selected_recipe + .as_ref() + .map(|s| s != name) + .unwrap_or(false) + { + self.imgs.nothing + } else { + match state.selected_recipe { + None => self.imgs.nothing, + Some(_) => self.imgs.selection, + } + }, + ); + // Recipe Button + let button = if i == 0 { + button.mid_top_with_margin_on(state.ids.align_rec, 2.0) + } else { + button.mid_bottom_with_margin_on(state.ids.recipe_names[i - 1], -25.0) + }; + if button + .label(recipe.output.0.name()) + .w_h(130.0, 20.0) + .hover_image(self.imgs.selection_hover) + .press_image(self.imgs.selection_press) + .label_color(can_perform.then_some(TEXT_COLOR).unwrap_or(TEXT_GRAY_COLOR)) + .label_font_size(self.fonts.cyri.scale(12)) + .label_font_id(self.fonts.cyri.conrod_id) + .label_y(conrod_core::position::Relative::Scalar(2.0)) + .set(state.ids.recipe_names[i], ui) + .was_clicked() + { + if state + .selected_recipe + .as_ref() + .map(|s| s == name) + .unwrap_or(false) + { + state.update(|s| s.selected_recipe = None); + } else { + state.update(|s| s.selected_recipe = Some(name.clone())); + } + } + // Image BG + /*Rectangle::fill_with([10.0, 10.0], color::TRANSPARENT) + .w_h(20.0, 20.0) + .mid_left_of(state.ids.recipe_names[i]) + .set(state.ids.recipe_img_frame[i], ui); + //Item Image + Image::new( + self.item_imgs + .img_id_or_not_found_img((&recipe.output.0).into()), + ) + .w_h(18.0, 18.0) + .color( + can_perform + .then_some(Some(TEXT_COLOR)) + .unwrap_or(Some(TEXT_GRAY_COLOR)), + ) + .middle_of(state.ids.recipe_img_frame[i]) + .set(state.ids.recipe_img[i], ui);*/ + } + + //Ingredients + if let Some(recipe) = state + .selected_recipe + .as_ref() + .and_then(|r| self.client.recipe_book().get(r.as_str())) + { + // Title + Text::new(&recipe.output.0.name().to_string()) + .mid_top_with_margin_on(state.ids.align_ing, -22.0) + .font_id(self.fonts.cyri.conrod_id) + .font_size(self.fonts.cyri.scale(14)) + .color(TEXT_COLOR) + .parent(state.ids.window) + .set(state.ids.title_ing, ui); + // Ingredient images with tooltip + if state.ids.ingredient_frame.len() < recipe.inputs().len() { + state.update(|state| { + state + .ids + .ingredient_frame + .resize(recipe.inputs().len(), &mut ui.widget_id_generator()) + }); + }; + if state.ids.ingredients.len() < recipe.inputs().len() { + state.update(|state| { + state + .ids + .ingredients + .resize(recipe.inputs().len(), &mut ui.widget_id_generator()) + }); + }; + if state.ids.ingredient_img.len() < recipe.inputs().len() { + state.update(|state| { + state + .ids + .ingredient_img + .resize(recipe.inputs().len(), &mut ui.widget_id_generator()) + }); + }; + if state.ids.req_text.len() < recipe.inputs().len() { + state.update(|state| { + state + .ids + .req_text + .resize(recipe.inputs().len(), &mut ui.widget_id_generator()) + }); + }; + // Widget generation for every ingredient + for (i, (item, amount)) in recipe.inputs.iter().enumerate() { + // Grey color for images and text if their amount is too low to craft the item + let col = if self.inventory.item_count(item) as f32 / *amount as f32 >= 1.0 { + TEXT_COLOR + } else { + TEXT_DULL_RED_COLOR + }; + // Slot BG + let frame_pos = if i == 0 { + state.ids.ingredients_txt + } else { + state.ids.ingredient_frame[i - 1] + }; + // add a larger offset for the the first ingredient and the "Required Text for + // Catalysts/Tools" + let frame_offset = if i == 0 { + 10.0 + } else if *amount == 0 { + 5.0 + } else { + 0.0 + }; + let frame = Image::new(self.imgs.inv_slot).w_h(25.0, 25.0); + let frame = if *amount == 0 { + frame.down_from(state.ids.req_text[i], 10.0 + frame_offset) + } else { + frame.down_from(frame_pos, 10.0 + frame_offset) + }; + frame.set(state.ids.ingredient_frame[i], ui); + //Item Image + let title = item.name(); + let desc = item.description(); + Button::image(self.item_imgs.img_id_or_not_found_img(item.into())) + .w_h(22.0, 22.0) + .middle_of(state.ids.ingredient_frame[i]) + //.image_color(col) + .with_tooltip(self.tooltip_manager, title, desc, &item_tooltip) + .set(state.ids.ingredient_img[i], ui); + // Ingredients text and amount + // Don't show inventory amounts above 999 to avoid the widget clipping + let over9k = "999+"; + let in_inv: &str = &self.inventory.item_count(item).to_string(); + // Show Ingredients + // Align "Required" Text below last ingredient + if *amount == 0 { + // Catalysts/Tools + Text::new(&self.localized_strings.get("hud.crafting.tool_cata")) + .down_from(state.ids.ingredient_frame[i - 1], 20.0) + .font_id(self.fonts.cyri.conrod_id) + .font_size(self.fonts.cyri.scale(14)) + .color(col) + .set(state.ids.req_text[i], ui); + Text::new(item.name()) + .right_from(state.ids.ingredient_frame[i], 10.0) + .font_id(self.fonts.cyri.conrod_id) + .font_size(self.fonts.cyri.scale(14)) + .color(col) + .set(state.ids.ingredients[i], ui); + } else { + // Ingredients + let input = format!( + "{}x {} ({})", + amount, + item.name(), + if self.inventory.item_count(item) > 999 { + over9k + } else { + in_inv + } + ); + // Ingredient Text + Text::new(&input) + .right_from(state.ids.ingredient_frame[i], 10.0) + .font_id(self.fonts.cyri.conrod_id) + .font_size(self.fonts.cyri.scale(14)) + .color(col) + .set(state.ids.ingredients[i], ui); + } + } + } + + let ids = &state.ids; + // Scrollbars + Scrollbar::y_axis(ids.align_rec) + .thickness(5.0) + .rgba(0.33, 0.33, 0.33, 1.0) + .set(ids.scrollbar_rec, ui); + Scrollbar::y_axis(ids.align_ing) + .thickness(5.0) + .rgba(0.33, 0.33, 0.33, 1.0) + .set(ids.scrollbar_ing, ui); + + // Title Recipes and Ingredients + Text::new(&self.localized_strings.get("hud.crafting.recipes")) + .mid_top_with_margin_on(ids.align_rec, -22.0) + .font_id(self.fonts.cyri.conrod_id) + .font_size(self.fonts.cyri.scale(14)) + .color(TEXT_COLOR) + .parent(ids.window) + .set(ids.title_rec, ui); + + events + } +} diff --git a/voxygen/src/hud/img_ids.rs b/voxygen/src/hud/img_ids.rs index 50fff76512..17e87924de 100644 --- a/voxygen/src/hud/img_ids.rs +++ b/voxygen/src/hud/img_ids.rs @@ -1,16 +1,15 @@ -use crate::ui::img_ids::{BlankGraphic, ImageGraphic, VoxelGraphic, VoxelPixArtGraphic}; +use crate::ui::img_ids::{BlankGraphic, ImageGraphic, VoxelPixArtGraphic}; // TODO: Combine with image_ids, see macro definition rotation_image_ids! { pub struct ImgsRot { - // Tooltip Test - tt_side: "voxygen/element/frames/tt_test_edge", - tt_corner: "voxygen/element/frames/tt_test_corner_tr", - indicator_mmap_small: "voxygen.element.buttons.indicator_mmap_small", + // Tooltip Test + tt_side: "voxygen/element/frames/tt_test_edge", + tt_corner: "voxygen/element/frames/tt_test_corner_tr", ////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -54,6 +53,10 @@ image_ids! { ////////////////////////////////////////////////////////////////////////////////////////////////////// + // Selection Frame + selection: "voxygen.element.frames.selection", + selection_hover: "voxygen.element.frames.selection_hover", + selection_press: "voxygen.element.frames.selection_press", // Social Window social_button: "voxygen.element.buttons.social_tab", @@ -62,6 +65,16 @@ image_ids! { social_button_press: "voxygen.element.buttons.social_tab_press", social_frame: "voxygen.element.frames.social_frame", + // Crafting Window + + crafting_window: "voxygen.element.misc_bg.crafting", + crafting_frame: "voxygen.element.misc_bg.crafting_frame", + crafting_icon_bordered: "voxygen.element.icons.anvil", + crafting_icon: "voxygen.element.buttons.anvil", + crafting_icon_hover: "voxygen.element.buttons.anvil_hover", + crafting_icon_press: "voxygen.element.buttons.anvil_press", + + // Chat-Arrows chat_arrow: "voxygen.element.buttons.arrow_down", chat_arrow_mo: "voxygen.element.buttons.arrow_down_hover", @@ -115,7 +128,7 @@ image_ids! { staff_m2: "voxygen.element.icons.staff_m2", flyingrod_m1: "voxygen.element.icons.debug_wand_m1", flyingrod_m2: "voxygen.element.icons.debug_wand_m2", - charge: "voxygen.element.icons.skill_charge_3", + sword_pierce: "voxygen.element.icons.skill_sword_pierce", hammerleap: "voxygen.element.icons.skill_hammerleap", axespin: "voxygen.element.icons.skill_axespin", diff --git a/voxygen/src/hud/minimap.rs b/voxygen/src/hud/minimap.rs index b696c2c66b..28d27563e7 100644 --- a/voxygen/src/hud/minimap.rs +++ b/voxygen/src/hud/minimap.rs @@ -109,7 +109,7 @@ impl<'a> Widget for MiniMap<'a> { if self.show.mini_map { Image::new(self.imgs.mmap_frame) .w_h(174.0 * SCALE, 190.0 * SCALE) - .top_right_with_margins_on(ui.window, 0.0, 5.0) + .top_right_with_margins_on(ui.window, 5.0, 5.0) .color(Some(UI_MAIN)) .set(state.ids.mmap_frame, ui); Image::new(self.imgs.mmap_frame_2) diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index 4177177ca8..89bf34089c 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -1,6 +1,7 @@ mod bag; mod buttons; mod chat; +mod crafting; mod esc_menu; mod hotbar; mod img_ids; @@ -25,6 +26,7 @@ use bag::Bag; use buttons::Buttons; use chat::Chat; use chrono::NaiveTime; +use crafting::Crafting; use esc_menu::EscMenu; use img_ids::Imgs; use item_imgs::ItemImgs; @@ -62,6 +64,8 @@ use vek::*; const XP_COLOR: Color = Color::Rgba(0.59, 0.41, 0.67, 1.0); const TEXT_COLOR: Color = Color::Rgba(1.0, 1.0, 1.0, 1.0); +const TEXT_GRAY_COLOR: Color = Color::Rgba(0.5, 0.5, 0.5, 1.0); +const TEXT_DULL_RED_COLOR: Color = Color::Rgba(0.56, 0.2, 0.2, 1.0); const TEXT_BG: Color = Color::Rgba(0.0, 0.0, 0.0, 1.0); //const TEXT_COLOR_GREY: Color = Color::Rgba(1.0, 1.0, 1.0, 0.5); const MENU_BG: Color = Color::Rgba(0.0, 0.0, 0.0, 0.4); @@ -199,6 +203,7 @@ widget_ids! { esc_menu, small_window, social_window, + crafting_window, settings_window, // Free look indicator @@ -287,6 +292,7 @@ pub enum Event { ChangeFreeLookBehavior(PressBehavior), ChangeAutoWalkBehavior(PressBehavior), ChangeStopAutoWalkOnInput(bool), + CraftRecipe(String), } // TODO: Are these the possible layouts we want? @@ -337,6 +343,7 @@ pub struct Show { ui: bool, intro: bool, help: bool, + crafting: bool, debug: bool, bag: bool, social: bool, @@ -355,29 +362,50 @@ pub struct Show { } impl Show { fn bag(&mut self, open: bool) { - self.bag = open; - self.map = false; - self.want_grab = !open; + if !self.esc_menu { + self.bag = open; + self.map = false; + self.want_grab = !open; + } } fn toggle_bag(&mut self) { self.bag(!self.bag); } fn map(&mut self, open: bool) { - self.map = open; - self.bag = false; - self.want_grab = !open; + if !self.esc_menu { + self.map = open; + self.bag = false; + self.crafting = false; + self.social = false; + self.spell = false; + self.want_grab = !open; + } } fn social(&mut self, open: bool) { - self.social = open; - self.spell = false; - self.want_grab = !open; + if !self.esc_menu { + self.social = open; + self.crafting = false; + self.spell = false; + self.want_grab = !open; + } + } + + fn crafting(&mut self, open: bool) { + if !self.esc_menu { + self.crafting = open; + self.bag = open; + self.want_grab = !open; + } } fn spell(&mut self, open: bool) { - self.social = false; - self.spell = open; - self.want_grab = !open; + if !self.esc_menu { + self.social = false; + self.crafting = false; + self.spell = open; + self.want_grab = !open; + } } fn toggle_map(&mut self) { self.map(!self.map) } @@ -385,15 +413,18 @@ impl Show { fn toggle_mini_map(&mut self) { self.mini_map = !self.mini_map; } fn settings(&mut self, open: bool) { - self.open_windows = if open { - Windows::Settings - } else { - Windows::None - }; - self.bag = false; - self.social = false; - self.spell = false; - self.want_grab = !open; + if !self.esc_menu { + self.open_windows = if open { + Windows::Settings + } else { + Windows::None + }; + self.bag = false; + self.social = false; + self.crafting = false; + self.spell = false; + self.want_grab = !open; + } } fn toggle_settings(&mut self) { @@ -412,6 +443,7 @@ impl Show { || self.esc_menu || self.map || self.social + || self.crafting || self.spell || self.help || self.intro @@ -427,6 +459,7 @@ impl Show { self.map = false; self.social = false; self.spell = false; + self.crafting = false; self.open_windows = Windows::None; self.want_grab = true; @@ -458,6 +491,8 @@ impl Show { self.spell = false; } + fn toggle_crafting(&mut self) { self.crafting(!self.crafting) } + fn open_social_tab(&mut self, social_tab: SocialTab) { self.social_tab = social_tab; self.spell = false; @@ -557,6 +592,7 @@ impl Hud { esc_menu: false, open_windows: Windows::None, map: false, + crafting: false, ui: true, social: false, spell: false, @@ -1478,6 +1514,7 @@ impl Hud { Some(buttons::Event::ToggleSocial) => self.show.toggle_social(), Some(buttons::Event::ToggleSpell) => self.show.toggle_spell(), Some(buttons::Event::ToggleMap) => self.show.toggle_map(), + Some(buttons::Event::ToggleCrafting) => self.show.toggle_crafting(), None => {}, } } @@ -1535,7 +1572,6 @@ impl Hud { } } } - // Skillbar // Get player stats let ecs = client.state().ecs(); @@ -1583,6 +1619,35 @@ impl Hud { .set(self.ids.skillbar, ui_widgets); } + // Crafting + if self.show.crafting { + if let Some(inventory) = inventories.get(entity) { + for event in Crafting::new( + //&self.show, + client, + &self.imgs, + &self.fonts, + &self.voxygen_i18n, + &self.rot_imgs, + tooltip_manager, + &self.item_imgs, + &inventory, + ) + .set(self.ids.crafting_window, ui_widgets) + { + match event { + crafting::Event::CraftRecipe(r) => { + events.push(Event::CraftRecipe(r)); + }, + crafting::Event::Close => { + self.show.crafting(false); + self.force_ungrab = true; + }, + } + } + } + } + // Don't put NPC messages in chat box. self.new_messages .retain(|m| !matches!(m.chat_type, comp::ChatType::Npc(_, _))); @@ -2071,6 +2136,10 @@ impl Hud { self.show.toggle_social(); true }, + GameInput::Crafting if state => { + self.show.toggle_crafting(); + true + }, GameInput::Spellbook if state => { self.show.toggle_spell(); true diff --git a/voxygen/src/hud/skillbar.rs b/voxygen/src/hud/skillbar.rs index 54a3a9e032..2e41c7a135 100644 --- a/voxygen/src/hud/skillbar.rs +++ b/voxygen/src/hud/skillbar.rs @@ -623,26 +623,7 @@ impl<'a> Widget for Skillbar<'a> { _ => self.imgs.nothing, }, ) // Insert Icon here - .w( - match self.loadout.active_item.as_ref().map(|i| &i.item.kind) { - Some(ItemKind::Tool(Tool { kind, .. })) => match kind { - ToolKind::Bow(_) => 30.0 * scale, - ToolKind::Staff(_) => 32.0 * scale, - _ => 38.0 * scale, - }, - _ => 38.0 * scale, - }, - ) - .h( - match self.loadout.active_item.as_ref().map(|i| &i.item.kind) { - Some(ItemKind::Tool(Tool { kind, .. })) => match kind { - ToolKind::Bow(_) => 30.0 * scale, - ToolKind::Staff(_) => 32.0 * scale, - _ => 38.0 * scale, - }, - _ => 38.0 * scale, - }, - ) + .w_h(32.0 * scale, 32.0 * scale) .middle_of(state.ids.m1_slot_bg) .set(state.ids.m1_content, ui); // M2 Slot @@ -704,7 +685,7 @@ impl<'a> Widget for Skillbar<'a> { .middle_of(state.ids.m2_slot) .set(state.ids.m2_slot_bg, ui); Button::image(match tool_kind { - Some(ToolKind::Sword(_)) => self.imgs.charge, + Some(ToolKind::Sword(_)) => self.imgs.twohsword_m2, Some(ToolKind::Dagger(_)) => self.imgs.onehdagger_m2, Some(ToolKind::Shield(_)) => self.imgs.onehshield_m2, Some(ToolKind::Hammer(_)) => self.imgs.hammerleap, @@ -714,17 +695,8 @@ impl<'a> Widget for Skillbar<'a> { Some(ToolKind::Staff(_)) => self.imgs.staff_m2, Some(ToolKind::Debug(DebugKind::Boost)) => self.imgs.flyingrod_m2, _ => self.imgs.nothing, - }) // Insert Icon here - .w(match tool_kind { - Some(ToolKind::Staff(_)) => 30.0 * scale, - Some(ToolKind::Bow(_)) => 30.0 * scale, - _ => 38.0 * scale, - }) - .h(match tool_kind { - Some(ToolKind::Staff(_)) => 30.0 * scale, - Some(ToolKind::Bow(_)) => 30.0 * scale, - _ => 38.0 * scale, }) + .w_h(32.0 * scale, 32.0 * scale) .middle_of(state.ids.m2_slot_bg) .image_color(match tool_kind { Some(ToolKind::Sword(_)) => { diff --git a/voxygen/src/menu/char_selection/ui.rs b/voxygen/src/menu/char_selection/ui.rs index 01a8d47cd0..9ae1c3288d 100644 --- a/voxygen/src/menu/char_selection/ui.rs +++ b/voxygen/src/menu/char_selection/ui.rs @@ -182,9 +182,6 @@ image_ids! { charlist_frame: "voxygen.element.frames.window_4", server_frame: "voxygen.element.frames.server_frame", - selection: "voxygen.element.frames.selection", - selection_hover: "voxygen.element.frames.selection_hover", - selection_press: "voxygen.element.frames.selection_press", // Info Window info_frame: "voxygen.element.frames.info_frame", @@ -195,6 +192,9 @@ image_ids! { delete_button_press: "voxygen.element.buttons.x_red_press", + selection: "voxygen.element.frames.selection", + selection_hover: "voxygen.element.frames.selection_hover", + selection_press: "voxygen.element.frames.selection_press", name_input: "voxygen.element.misc_bg.textbox_mid", @@ -241,8 +241,7 @@ image_ids! { } rotation_image_ids! { pub struct ImgsRot { - - + // Tooltip Test tt_side: "voxygen/element/frames/tt_test_edge", tt_corner: "voxygen/element/frames/tt_test_corner_tr", diff --git a/voxygen/src/menu/main/ui.rs b/voxygen/src/menu/main/ui.rs index a8d037f482..9ba01eb8a0 100644 --- a/voxygen/src/menu/main/ui.rs +++ b/voxygen/src/menu/main/ui.rs @@ -35,6 +35,7 @@ widget_ids! { alpha_text, banner, banner_top, + gears, // Disclaimer //disc_window, //disc_text_1, @@ -98,7 +99,12 @@ image_ids! { button_press: "voxygen.element.buttons.button_press", input_bg: "voxygen.element.misc_bg.textbox_mid", //disclaimer: "voxygen.element.frames.disclaimer", - + // Animation + f1: "voxygen.element.animation.gears.1", + f2: "voxygen.element.animation.gears.2", + f3: "voxygen.element.animation.gears.3", + f4: "voxygen.element.animation.gears.4", + f5: "voxygen.element.animation.gears.5", nothing: (), @@ -107,7 +113,7 @@ image_ids! { rotation_image_ids! { pub struct ImgsRot { - + // Tooltip Test tt_side: "voxygen/element/frames/tt_test_edge", @@ -155,6 +161,7 @@ pub struct MainMenuUi { show_servers: bool, //show_disclaimer: bool, time: f32, + anim_timer: f32, bg_img_id: conrod_core::image::Id, voxygen_i18n: std::sync::Arc, fonts: ConrodVoxygenFonts, @@ -221,6 +228,7 @@ impl MainMenuUi { show_servers: false, connect: false, time: 0.0, + anim_timer: 0.0, //show_disclaimer: global_state.settings.show_disclaimer, bg_img_id, voxygen_i18n, @@ -275,6 +283,23 @@ impl MainMenuUi { .middle_of(ui_widgets.window) .set(self.ids.bg, ui_widgets); + if self.connect { + self.anim_timer = (self.anim_timer + dt.as_secs_f32()) * 1.05; // Linear time function with Anim-Speed Factor + if self.anim_timer >= 4.0 { + self.anim_timer = 0.0 // Reset timer at last frame to loop + }; + Image::new(match self.anim_timer.round() as i32 { + 0 => self.imgs.f1, + 1 => self.imgs.f2, + 2 => self.imgs.f3, + 3 => self.imgs.f4, + _ => self.imgs.f5, + }) + .w_h(74.0, 62.0) + .bottom_right_with_margins_on(self.ids.bg, 10.0, 10.0) + .set(self.ids.gears, ui_widgets); + }; + // Version displayed top right corner Text::new(&version) .color(TEXT_COLOR) diff --git a/voxygen/src/scene/terrain.rs b/voxygen/src/scene/terrain.rs index e40810d398..76bd95e974 100644 --- a/voxygen/src/scene/terrain.rs +++ b/voxygen/src/scene/terrain.rs @@ -338,6 +338,18 @@ fn sprite_config_for(kind: BlockKind) -> Option { variations: 2, wind_sway: 0.0, }), + BlockKind::Stones => Some(SpriteConfig { + variations: 3, + wind_sway: 0.0, + }), + BlockKind::Twigs => Some(SpriteConfig { + variations: 3, + wind_sway: 0.0, + }), + BlockKind::ShinyGem => Some(SpriteConfig { + variations: 3, + wind_sway: 0.0, + }), _ => None, } } @@ -2646,6 +2658,81 @@ impl Terrain { Vec3::one(), ), ), + /* Stones */ + ( + (BlockKind::Stones, 0), + make_models( + "voxygen.voxel.sprite.rocks.rock-0", + Vec3::new(-3.0, -3.5, 0.0), + Vec3::one(), + ), + ), + ( + (BlockKind::Stones, 1), + make_models( + "voxygen.voxel.sprite.rocks.rock-1", + Vec3::new(-4.5, -5.5, 0.0), + Vec3::one(), + ), + ), + ( + (BlockKind::Stones, 2), + make_models( + "voxygen.voxel.sprite.rocks.rock-2", + Vec3::new(-4.5, -4.5, 0.0), + Vec3::one(), + ), + ), + /* Twigs */ + ( + (BlockKind::Twigs, 0), + make_models( + "voxygen.voxel.sprite.twigs.twigs-0", + Vec3::new(-3.5, -3.5, 0.0), + Vec3::one(), + ), + ), + ( + (BlockKind::Twigs, 1), + make_models( + "voxygen.voxel.sprite.twigs.twigs-1", + Vec3::new(-2.0, -1.5, 0.0), + Vec3::one(), + ), + ), + ( + (BlockKind::Twigs, 2), + make_models( + "voxygen.voxel.sprite.twigs.twigs-2", + Vec3::new(-4.0, -4.0, 0.0), + Vec3::one(), + ), + ), + // Shiny Gems + ( + (BlockKind::ShinyGem, 0), + make_models( + "voxygen.voxel.sprite.gem.gem_blue", + Vec3::new(-2.0, -3.0, 0.0), + Vec3::one(), + ), + ), + ( + (BlockKind::ShinyGem, 1), + make_models( + "voxygen.voxel.sprite.gem.gem_green", + Vec3::new(-2.0, -3.0, 0.0), + Vec3::one(), + ), + ), + ( + (BlockKind::ShinyGem, 2), + make_models( + "voxygen.voxel.sprite.gem.gem_red", + Vec3::new(-3.0, -2.0, -2.0), + Vec3::one(), + ), + ), ] .into_iter() .collect(), diff --git a/voxygen/src/session.rs b/voxygen/src/session.rs index d0c1c7acf9..b2a042f1af 100644 --- a/voxygen/src/session.rs +++ b/voxygen/src/session.rs @@ -936,6 +936,9 @@ impl PlayState for SessionState { HudEvent::ChangeStopAutoWalkOnInput(state) => { global_state.settings.gameplay.stop_auto_walk_on_input = state; }, + HudEvent::CraftRecipe(r) => { + self.client.borrow_mut().craft_recipe(&r); + }, } } diff --git a/voxygen/src/settings.rs b/voxygen/src/settings.rs index 373515a957..fb6f787e96 100644 --- a/voxygen/src/settings.rs +++ b/voxygen/src/settings.rs @@ -141,6 +141,7 @@ impl ControlSettings { GameInput::Map => KeyMouse::Key(VirtualKeyCode::M), GameInput::Bag => KeyMouse::Key(VirtualKeyCode::B), GameInput::Social => KeyMouse::Key(VirtualKeyCode::O), + GameInput::Crafting => KeyMouse::Key(VirtualKeyCode::C), GameInput::Spellbook => KeyMouse::Key(VirtualKeyCode::P), GameInput::Settings => KeyMouse::Key(VirtualKeyCode::N), GameInput::Help => KeyMouse::Key(VirtualKeyCode::F1), @@ -178,7 +179,63 @@ impl Default for ControlSettings { keybindings: HashMap::new(), inverse_keybindings: HashMap::new(), }; - for game_input in GameInput::iterator() { + // Sets the initial keybindings for those GameInputs. If a new one is created in + // future, you'll have to update default_binding, and you should update this vec + // too. + let game_inputs = vec![ + GameInput::Primary, + GameInput::Secondary, + GameInput::ToggleCursor, + GameInput::MoveForward, + GameInput::MoveBack, + GameInput::MoveLeft, + GameInput::MoveRight, + GameInput::Jump, + GameInput::Sit, + GameInput::Dance, + GameInput::Glide, + GameInput::Climb, + GameInput::ClimbDown, + GameInput::Swim, + //GameInput::WallLeap, + GameInput::ToggleLantern, + GameInput::Mount, + GameInput::Enter, + GameInput::Command, + GameInput::Escape, + GameInput::Map, + GameInput::Bag, + GameInput::Social, + GameInput::Crafting, + GameInput::Spellbook, + GameInput::Settings, + GameInput::ToggleInterface, + GameInput::Help, + GameInput::ToggleDebug, + GameInput::Fullscreen, + GameInput::Screenshot, + GameInput::ToggleIngameUi, + GameInput::Roll, + GameInput::Respawn, + GameInput::Interact, + GameInput::ToggleWield, + //GameInput::Charge, + GameInput::FreeLook, + GameInput::AutoWalk, + GameInput::CycleCamera, + GameInput::Slot1, + GameInput::Slot2, + GameInput::Slot3, + GameInput::Slot4, + GameInput::Slot5, + GameInput::Slot6, + GameInput::Slot7, + GameInput::Slot8, + GameInput::Slot9, + GameInput::Slot10, + GameInput::SwapLoadout, + ]; + for game_input in game_inputs { new_settings.insert_binding(game_input, ControlSettings::default_binding(game_input)); } new_settings @@ -254,6 +311,7 @@ pub mod con_settings { pub quest_log: Button, pub character_window: Button, pub social: Button, + pub crafting: Button, pub spellbook: Button, pub settings: Button, pub help: Button, @@ -343,6 +401,7 @@ pub mod con_settings { quest_log: Button::Simple(GilButton::Unknown), character_window: Button::Simple(GilButton::Unknown), social: Button::Simple(GilButton::Unknown), + crafting: Button::Simple(GilButton::Unknown), spellbook: Button::Simple(GilButton::Unknown), settings: Button::Simple(GilButton::Unknown), help: Button::Simple(GilButton::Unknown), @@ -463,7 +522,7 @@ impl Default for GameplaySettings { intro_show: Intro::Show, xp_bar: XpBar::Always, shortcut_numbers: ShortcutNumbers::On, - bar_numbers: BarNumbers::Off, + bar_numbers: BarNumbers::Values, ui_scale: ScaleMode::RelativeToWindow([1920.0, 1080.0].into()), free_look_behavior: PressBehavior::Toggle, auto_walk_behavior: PressBehavior::Toggle, diff --git a/voxygen/src/window.rs b/voxygen/src/window.rs index b3b08e3259..73b75de715 100644 --- a/voxygen/src/window.rs +++ b/voxygen/src/window.rs @@ -49,6 +49,7 @@ pub enum GameInput { Map, Bag, Social, + Crafting, Spellbook, Settings, ToggleInterface, @@ -95,6 +96,7 @@ impl GameInput { GameInput::Map => "gameinput.map", GameInput::Bag => "gameinput.bag", GameInput::Social => "gameinput.social", + GameInput::Crafting => "gameinput.crafting", GameInput::Spellbook => "gameinput.spellbook", GameInput::Settings => "gameinput.settings", GameInput::ToggleInterface => "gameinput.toggleinterface", diff --git a/world/src/block/mod.rs b/world/src/block/mod.rs index 5f1479f48e..7d3558e066 100644 --- a/world/src/block/mod.rs +++ b/world/src/block/mod.rs @@ -287,11 +287,14 @@ impl<'a> BlockGen<'a> { BlockKind::WhiteFlower, BlockKind::YellowFlower, BlockKind::Sunflower, - BlockKind::Mushroom, + BlockKind::Mushroom, //TODO: Better spawnrules BlockKind::LeafyPlant, BlockKind::Blueberry, BlockKind::LingonBerry, BlockKind::Fern, + /*BlockKind::Twigs, // TODO: Better spawnrules + *BlockKind::Stones, // TODO: Better spawnrules + *BlockKind::ShinyGem, // TODO: Better spawnrules */ ]; let grasses = [ BlockKind::LongGrass, From 1cdab4b4e81db7562fbef98b22562e1a72eab1bd Mon Sep 17 00:00:00 2001 From: PatatasDelPapa <43626645+PatatasDelPapa@users.noreply.github.com> Date: Tue, 14 Jul 2020 23:25:46 -0400 Subject: [PATCH 30/85] update es_la --- assets/voxygen/i18n/es_la.ron | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/assets/voxygen/i18n/es_la.ron b/assets/voxygen/i18n/es_la.ron index 3f8eb56bc5..092de3d000 100644 --- a/assets/voxygen/i18n/es_la.ron +++ b/assets/voxygen/i18n/es_la.ron @@ -155,6 +155,18 @@ https://account.veloren.net."#, "hud.press_key_to_toggle_keybindings_fmt": "Presiona {key} para alternar los controles del teclado", "hud.press_key_to_toggle_debug_info_fmt": "Presiona {key} para alternar la información de depuración", + // Chat outputs + "hud.chat.online_msg": "[{name}] se ha conectado.", + "hud.chat.offline_msg": "{name} se ha desconectado.", + "hud.chat.loot_msg": "Recogiste [{item}]", + "hud.chat.loot_fail": "Tu inventario está lleno!", + "hud.chat.goodbye": "Adiós!", + "hud.chat.connection_lost": "Conexión perdida. Expulsando en {time} segundos.", + + // SCT outputs + "hud.sct.experience": "{amount} Exp", + "hud.sct.block": "BLOQUEADO", + /// Respawn message "hud.press_key_to_respawn": r#"Presiona {key} para reaparecer en la ultima fogata que visitaste."#, @@ -252,12 +264,15 @@ objetos infundidos con magia?"#, "hud.settings.cumulated_damage": "Daño Acumulado", "hud.settings.incoming_damage": "Daño Recibido", "hud.settings.cumulated_incoming_damage": "Daño Recibido Acumulado", - "hud.settings.speech_bubble_dark_mode": "Burbujas de Dialogo en Modo Oscuro", + "hud.settings.speech_bubble": "Burbuja de Diálogo", + "hud.settings.speech_bubble_dark_mode": "Burbuja de Diálogo en Modo Oscuro", + "hud.settings.speech_bubble_icon": "Icono de Burbuja de Diálogo", "hud.settings.energybar_numbers": "Números de la Barras de Energia", "hud.settings.values": "Valores", "hud.settings.percentages": "Porcentajes", "hud.settings.chat": "Chat", "hud.settings.background_transparency": "Transparencia de Fondo", + "hud.settings.chat_character_name": "Nombres de Personajes en el chat", "hud.settings.pan_sensitivity": "Sensibilidad de Desplazamiento", "hud.settings.zoom_sensitivity": "Sensibilidad del Zoom", @@ -288,6 +303,8 @@ objetos infundidos con magia?"#, "hud.settings.audio_device": "Dispositivo de Audio", "hud.settings.awaitingkey": "Presiona una tecla...", + "hud.settings.unbound": "Ninguno", + "hud.settings.reset_keybinds": "Reestablecer a los valores predeterminados", "hud.social": "Social", "hud.social.online": "En Linea", @@ -299,7 +316,7 @@ objetos infundidos con magia?"#, "hud.spell": "Hechizos", "hud.free_look_indicator": "Vista libre activa", - "hud.auto_walk_indicator": "Auto caminar activada", + "hud.auto_walk_indicator": "Auto caminar activado", /// End HUD section From a3326b574468ebc2dc6bb170c0b0aa60d3422b6d Mon Sep 17 00:00:00 2001 From: lausek Date: Wed, 15 Jul 2020 14:54:53 +0200 Subject: [PATCH 31/85] fix #611; add distance check when mounting pet --- common/src/comp/mod.rs | 2 +- common/src/comp/player.rs | 1 + voxygen/src/session.rs | 39 +++++++++++++++++++++++---------------- 3 files changed, 25 insertions(+), 17 deletions(-) diff --git a/common/src/comp/mod.rs b/common/src/comp/mod.rs index 3efd7e9381..8ab32575f6 100644 --- a/common/src/comp/mod.rs +++ b/common/src/comp/mod.rs @@ -43,7 +43,7 @@ pub use last::Last; pub use location::{Waypoint, WaypointArea}; pub use misc::Object; pub use phys::{Collider, ForceUpdate, Gravity, Mass, Ori, PhysicsState, Pos, Scale, Sticky, Vel}; -pub use player::Player; +pub use player::{Player, MAX_MOUNT_RANGE_SQR}; pub use projectile::Projectile; pub use skills::{Skill, SkillGroup, SkillGroupType, SkillSet}; pub use stats::{Exp, HealthChange, HealthSource, Level, Stats}; diff --git a/common/src/comp/player.rs b/common/src/comp/player.rs index 0527b20284..3c4bbe2002 100644 --- a/common/src/comp/player.rs +++ b/common/src/comp/player.rs @@ -4,6 +4,7 @@ use specs::{Component, FlaggedStorage, NullStorage}; use specs_idvs::IdvStorage; const MAX_ALIAS_LEN: usize = 32; +pub const MAX_MOUNT_RANGE_SQR: i32 = 20000; #[derive(Clone, Debug, Serialize, Deserialize)] pub struct Player { diff --git a/voxygen/src/session.rs b/voxygen/src/session.rs index b2a042f1af..4467882f47 100644 --- a/voxygen/src/session.rs +++ b/voxygen/src/session.rs @@ -15,7 +15,7 @@ use common::{ assets::{load_expect, load_watched, watch}, clock::Clock, comp, - comp::{ChatMsg, ChatType, InventoryUpdateEvent, Pos, Vel, MAX_PICKUP_RANGE_SQR}, + comp::{ChatMsg, ChatType, InventoryUpdateEvent, Pos, Vel, MAX_MOUNT_RANGE_SQR, MAX_PICKUP_RANGE_SQR}, event::EventBus, msg::ClientState, terrain::{Block, BlockKind}, @@ -430,25 +430,32 @@ impl PlayState for SessionState { .copied(); if let Some(player_pos) = player_pos { // Find closest mountable entity - let closest_mountable = ( + let mut closest_mountable: Option<(specs::Entity, i32)> = None; + + for (uid, pos, ms) in ( &client.state().ecs().entities(), &client.state().ecs().read_storage::(), &client.state().ecs().read_storage::(), - ) - .join() - .filter(|(_, _, ms)| { - if let comp::MountState::Unmounted = ms { - true - } else { - false - } - }) - .min_by_key(|(_, pos, _)| { - (player_pos.0.distance_squared(pos.0) * 1000.0) as i32 - }) - .map(|(uid, _, _)| uid); + ).join() { + if comp::MountState::Unmounted != *ms { + continue; + } - if let Some(mountee_entity) = closest_mountable { + let dist = (player_pos.0.distance_squared(pos.0) * 1000.0) as i32; + if MAX_MOUNT_RANGE_SQR < dist { + continue; + } + + if let Some(previous) = closest_mountable.as_mut() { + if dist < previous.1 { + *previous = (uid, dist); + } + } else { + closest_mountable = Some((uid, dist)); + } + } + + if let Some((mountee_entity, _)) = closest_mountable { client.mount(mountee_entity); } } From 05cc6f222bac905b6a0931a3f7d5b9659bfbeba8 Mon Sep 17 00:00:00 2001 From: lausek Date: Wed, 15 Jul 2020 15:31:42 +0200 Subject: [PATCH 32/85] avoid comparing to own entity when mounting; fix coding style --- voxygen/src/session.rs | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/voxygen/src/session.rs b/voxygen/src/session.rs index 4467882f47..d6d1b48148 100644 --- a/voxygen/src/session.rs +++ b/voxygen/src/session.rs @@ -15,7 +15,10 @@ use common::{ assets::{load_expect, load_watched, watch}, clock::Clock, comp, - comp::{ChatMsg, ChatType, InventoryUpdateEvent, Pos, Vel, MAX_MOUNT_RANGE_SQR, MAX_PICKUP_RANGE_SQR}, + comp::{ + ChatMsg, ChatType, InventoryUpdateEvent, Pos, Vel, MAX_MOUNT_RANGE_SQR, + MAX_PICKUP_RANGE_SQR, + }, event::EventBus, msg::ClientState, terrain::{Block, BlockKind}, @@ -432,26 +435,30 @@ impl PlayState for SessionState { // Find closest mountable entity let mut closest_mountable: Option<(specs::Entity, i32)> = None; - for (uid, pos, ms) in ( + for (entity, pos, ms) in ( &client.state().ecs().entities(), &client.state().ecs().read_storage::(), &client.state().ecs().read_storage::(), - ).join() { + ) + .join() + .filter(|(entity, _, _)| *entity != client.entity()) + { if comp::MountState::Unmounted != *ms { continue; } - let dist = (player_pos.0.distance_squared(pos.0) * 1000.0) as i32; - if MAX_MOUNT_RANGE_SQR < dist { + let dist = + (player_pos.0.distance_squared(pos.0) * 1000.0) as i32; + if dist > MAX_MOUNT_RANGE_SQR { continue; } if let Some(previous) = closest_mountable.as_mut() { if dist < previous.1 { - *previous = (uid, dist); + *previous = (entity, dist); } } else { - closest_mountable = Some((uid, dist)); + closest_mountable = Some((entity, dist)); } } From 58cb98deaa01f0d8ddc049611d5cc028e97df5e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=A4rtens?= Date: Wed, 15 Jul 2020 00:18:04 +0200 Subject: [PATCH 33/85] use `type` to reduce complexity --- network/src/api.rs | 21 +++++------ network/src/participant.rs | 45 ++++++++++------------- network/src/scheduler.rs | 75 ++++++++++++++++---------------------- 3 files changed, 62 insertions(+), 79 deletions(-) diff --git a/network/src/api.rs b/network/src/api.rs index d31de577b2..39b47ad1ce 100644 --- a/network/src/api.rs +++ b/network/src/api.rs @@ -4,6 +4,7 @@ //! (cd network/examples/async_recv && RUST_BACKTRACE=1 cargo run) use crate::{ message::{self, partial_eq_bincode, IncomingMessage, MessageBuffer, OutgoingMessage}, + participant::{A2bStreamOpen, S2bShutdownBparticipant}, scheduler::Scheduler, types::{Mid, Pid, Prio, Promises, Sid}, }; @@ -30,8 +31,7 @@ use std::{ use tracing::*; use tracing_futures::Instrument; -type ParticipantCloseChannel = - mpsc::UnboundedSender<(Pid, oneshot::Sender>)>; +type A2sDisconnect = Arc>>>; /// Represents a Tcp or Udp or Mpsc address #[derive(Clone, Debug, Hash, PartialEq, Eq)] @@ -51,10 +51,10 @@ pub enum ProtocolAddr { pub struct Participant { local_pid: Pid, remote_pid: Pid, - a2b_steam_open_s: RwLock)>>, + a2b_stream_open_s: RwLock>, b2a_stream_opened_r: RwLock>, closed: Arc>>, - a2s_disconnect_s: Arc>>, + a2s_disconnect_s: A2sDisconnect, } /// `Streams` represents a channel to send `n` messages with a certain priority @@ -147,8 +147,7 @@ pub enum StreamError { /// [`connected`]: Network::connected pub struct Network { local_pid: Pid, - participant_disconnect_sender: - RwLock>>>>, + participant_disconnect_sender: RwLock>, listen_sender: RwLock>)>>, connect_sender: @@ -390,15 +389,15 @@ impl Participant { pub(crate) fn new( local_pid: Pid, remote_pid: Pid, - a2b_steam_open_s: mpsc::UnboundedSender<(Prio, Promises, oneshot::Sender)>, + a2b_stream_open_s: mpsc::UnboundedSender, b2a_stream_opened_r: mpsc::UnboundedReceiver, - a2s_disconnect_s: mpsc::UnboundedSender<(Pid, oneshot::Sender>)>, + a2s_disconnect_s: mpsc::UnboundedSender<(Pid, S2bShutdownBparticipant)>, closed: Arc>>, ) -> Self { Self { local_pid, remote_pid, - a2b_steam_open_s: RwLock::new(a2b_steam_open_s), + a2b_stream_open_s: RwLock::new(a2b_stream_open_s), b2a_stream_opened_r: RwLock::new(b2a_stream_opened_r), closed, a2s_disconnect_s: Arc::new(Mutex::new(Some(a2s_disconnect_s))), @@ -448,10 +447,10 @@ impl Participant { pub async fn open(&self, prio: u8, promises: Promises) -> Result { //use this lock for now to make sure that only one open at a time is made, // TODO: not sure if we can paralise that, check in future - let mut a2b_steam_open_s = self.a2b_steam_open_s.write().await; + let mut a2b_stream_open_s = self.a2b_stream_open_s.write().await; self.closed.read().await.clone()?; let (p2a_return_stream_s, p2a_return_stream_r) = oneshot::channel(); - a2b_steam_open_s + a2b_stream_open_s .send((prio, promises, p2a_return_stream_s)) .await .unwrap(); diff --git a/network/src/participant.rs b/network/src/participant.rs index 08fb58cc7e..901b10b4c7 100644 --- a/network/src/participant.rs +++ b/network/src/participant.rs @@ -25,6 +25,11 @@ use std::{ }; use tracing::*; +pub(crate) type A2bStreamOpen = (Prio, Promises, oneshot::Sender); +pub(crate) type S2bCreateChannel = (Cid, Sid, Protocols, Vec<(Cid, Frame)>, oneshot::Sender<()>); +pub(crate) type S2bShutdownBparticipant = oneshot::Sender>; +pub(crate) type B2sPrioStatistic = (Pid, u64, u64); + #[derive(Debug)] struct ChannelInfo { cid: Cid, @@ -42,15 +47,13 @@ struct StreamInfo { } #[derive(Debug)] -#[allow(clippy::type_complexity)] struct ControlChannels { - a2b_steam_open_r: mpsc::UnboundedReceiver<(Prio, Promises, oneshot::Sender)>, + a2b_stream_open_r: mpsc::UnboundedReceiver, b2a_stream_opened_s: mpsc::UnboundedSender, - s2b_create_channel_r: - mpsc::UnboundedReceiver<(Cid, Sid, Protocols, Vec<(Cid, Frame)>, oneshot::Sender<()>)>, + s2b_create_channel_r: mpsc::UnboundedReceiver, a2b_close_stream_r: mpsc::UnboundedReceiver, a2b_close_stream_s: mpsc::UnboundedSender, - s2b_shutdown_bparticipant_r: oneshot::Receiver>>, /* own */ + s2b_shutdown_bparticipant_r: oneshot::Receiver, /* own */ } #[derive(Debug)] @@ -75,21 +78,20 @@ impl BParticipant { metrics: Arc, ) -> ( Self, - mpsc::UnboundedSender<(Prio, Promises, oneshot::Sender)>, + mpsc::UnboundedSender, mpsc::UnboundedReceiver, - mpsc::UnboundedSender<(Cid, Sid, Protocols, Vec<(Cid, Frame)>, oneshot::Sender<()>)>, - oneshot::Sender>>, + mpsc::UnboundedSender, + oneshot::Sender, Arc>>, ) { - let (a2b_steam_open_s, a2b_steam_open_r) = - mpsc::unbounded::<(Prio, Promises, oneshot::Sender)>(); + let (a2b_steam_open_s, a2b_stream_open_r) = mpsc::unbounded::(); let (b2a_stream_opened_s, b2a_stream_opened_r) = mpsc::unbounded::(); let (a2b_close_stream_s, a2b_close_stream_r) = mpsc::unbounded(); let (s2b_shutdown_bparticipant_s, s2b_shutdown_bparticipant_r) = oneshot::channel(); let (s2b_create_channel_s, s2b_create_channel_r) = mpsc::unbounded(); let run_channels = Some(ControlChannels { - a2b_steam_open_r, + a2b_stream_open_r, b2a_stream_opened_s, s2b_create_channel_r, a2b_close_stream_r, @@ -120,7 +122,7 @@ impl BParticipant { ) } - pub async fn run(mut self, b2s_prio_statistic_s: mpsc::UnboundedSender<(Pid, u64, u64)>) { + pub async fn run(mut self, b2s_prio_statistic_s: mpsc::UnboundedSender) { //those managers that listen on api::Participant need an additional oneshot for // shutdown scenario, those handled by scheduler will be closed by it. let (shutdown_send_mgr_sender, shutdown_send_mgr_receiver) = oneshot::channel(); @@ -135,7 +137,7 @@ impl BParticipant { let run_channels = self.run_channels.take().unwrap(); futures::join!( self.open_mgr( - run_channels.a2b_steam_open_r, + run_channels.a2b_stream_open_r, run_channels.a2b_close_stream_s.clone(), a2p_msg_s.clone(), shutdown_open_mgr_receiver, @@ -175,7 +177,7 @@ impl BParticipant { mut prios: PrioManager, mut shutdown_send_mgr_receiver: oneshot::Receiver<()>, b2b_prios_flushed_s: oneshot::Sender<()>, - mut b2s_prio_statistic_s: mpsc::UnboundedSender<(Pid, u64, u64)>, + mut b2s_prio_statistic_s: mpsc::UnboundedSender, ) { //This time equals the MINIMUM Latency in average, so keep it down and //Todo: // make it configureable or switch to await E.g. Prio 0 = await, prio 50 @@ -407,16 +409,9 @@ impl BParticipant { self.running_mgr.fetch_sub(1, Ordering::Relaxed); } - #[allow(clippy::type_complexity)] async fn create_channel_mgr( &self, - s2b_create_channel_r: mpsc::UnboundedReceiver<( - Cid, - Sid, - Protocols, - Vec<(Cid, Frame)>, - oneshot::Sender<()>, - )>, + s2b_create_channel_r: mpsc::UnboundedReceiver, w2b_frames_s: mpsc::UnboundedSender<(Cid, Frame)>, ) { self.running_mgr.fetch_add(1, Ordering::Relaxed); @@ -461,7 +456,7 @@ impl BParticipant { async fn open_mgr( &self, - mut a2b_steam_open_r: mpsc::UnboundedReceiver<(Prio, Promises, oneshot::Sender)>, + mut a2b_stream_open_r: mpsc::UnboundedReceiver, a2b_close_stream_s: mpsc::UnboundedSender, a2p_msg_s: crossbeam_channel::Sender<(Prio, Sid, OutgoingMessage)>, shutdown_open_mgr_receiver: oneshot::Receiver<()>, @@ -474,7 +469,7 @@ impl BParticipant { let mut shutdown_open_mgr_receiver = shutdown_open_mgr_receiver.fuse(); //from api or shutdown signal while let Some((prio, promises, p2a_return_stream)) = select! { - next = a2b_steam_open_r.next().fuse() => next, + next = a2b_stream_open_r.next().fuse() => next, _ = shutdown_open_mgr_receiver => None, } { debug!(?prio, ?promises, "Got request to open a new steam"); @@ -512,7 +507,7 @@ impl BParticipant { /// called by api to get the result status async fn participant_shutdown_mgr( &self, - s2b_shutdown_bparticipant_r: oneshot::Receiver>>, + s2b_shutdown_bparticipant_r: oneshot::Receiver, b2b_prios_flushed_r: oneshot::Receiver<()>, mut mgr_to_shutdown: Vec>, ) { diff --git a/network/src/scheduler.rs b/network/src/scheduler.rs index 7917266638..931fabb2a1 100644 --- a/network/src/scheduler.rs +++ b/network/src/scheduler.rs @@ -2,9 +2,9 @@ use crate::{ api::{Participant, ProtocolAddr}, channel::Handshake, metrics::NetworkMetrics, - participant::BParticipant, + participant::{B2sPrioStatistic, BParticipant, S2bCreateChannel}, protocols::{Protocols, TcpProtocol, UdpProtocol}, - types::{Cid, Frame, Pid, Sid}, + types::Pid, }; use async_std::{ io, net, @@ -30,16 +30,6 @@ use std::{ use tracing::*; use tracing_futures::Instrument; -#[derive(Debug)] -#[allow(clippy::type_complexity)] -struct ParticipantInfo { - secret: u128, - s2b_create_channel_s: - mpsc::UnboundedSender<(Cid, Sid, Protocols, Vec<(Cid, Frame)>, oneshot::Sender<()>)>, - s2b_shutdown_bparticipant_s: - Option>>>, -} - /// Naming of Channels `x2x` /// - a: api /// - s: scheduler @@ -48,21 +38,33 @@ struct ParticipantInfo { /// - r: protocol /// - w: wire /// - c: channel/handshake + +#[derive(Debug)] +struct ParticipantInfo { + secret: u128, + s2b_create_channel_s: mpsc::UnboundedSender, + s2b_shutdown_bparticipant_s: + Option>>>, +} + +type A2sListen = (ProtocolAddr, oneshot::Sender>); +type A2sConnect = (ProtocolAddr, oneshot::Sender>); +type A2sDisconnect = (Pid, oneshot::Sender>); + #[derive(Debug)] struct ControlChannels { - a2s_listen_r: mpsc::UnboundedReceiver<(ProtocolAddr, oneshot::Sender>)>, - a2s_connect_r: - mpsc::UnboundedReceiver<(ProtocolAddr, oneshot::Sender>)>, + a2s_listen_r: mpsc::UnboundedReceiver, + a2s_connect_r: mpsc::UnboundedReceiver, a2s_scheduler_shutdown_r: oneshot::Receiver<()>, - a2s_disconnect_r: mpsc::UnboundedReceiver<(Pid, oneshot::Sender>)>, - b2s_prio_statistic_r: mpsc::UnboundedReceiver<(Pid, u64, u64)>, + a2s_disconnect_r: mpsc::UnboundedReceiver, + b2s_prio_statistic_r: mpsc::UnboundedReceiver, } #[derive(Debug, Clone)] struct ParticipantChannels { s2a_connected_s: mpsc::UnboundedSender, - a2s_disconnect_s: mpsc::UnboundedSender<(Pid, oneshot::Sender>)>, - b2s_prio_statistic_s: mpsc::UnboundedSender<(Pid, u64, u64)>, + a2s_disconnect_s: mpsc::UnboundedSender, + b2s_prio_statistic_s: mpsc::UnboundedSender, } #[derive(Debug)] @@ -80,26 +82,22 @@ pub struct Scheduler { } impl Scheduler { - #[allow(clippy::type_complexity)] pub fn new( local_pid: Pid, registry: Option<&Registry>, ) -> ( Self, - mpsc::UnboundedSender<(ProtocolAddr, oneshot::Sender>)>, - mpsc::UnboundedSender<(ProtocolAddr, oneshot::Sender>)>, + mpsc::UnboundedSender, + mpsc::UnboundedSender, mpsc::UnboundedReceiver, oneshot::Sender<()>, ) { - let (a2s_listen_s, a2s_listen_r) = - mpsc::unbounded::<(ProtocolAddr, oneshot::Sender>)>(); - let (a2s_connect_s, a2s_connect_r) = - mpsc::unbounded::<(ProtocolAddr, oneshot::Sender>)>(); + let (a2s_listen_s, a2s_listen_r) = mpsc::unbounded::(); + let (a2s_connect_s, a2s_connect_r) = mpsc::unbounded::(); let (s2a_connected_s, s2a_connected_r) = mpsc::unbounded::(); let (a2s_scheduler_shutdown_s, a2s_scheduler_shutdown_r) = oneshot::channel::<()>(); - let (a2s_disconnect_s, a2s_disconnect_r) = - mpsc::unbounded::<(Pid, oneshot::Sender>)>(); - let (b2s_prio_statistic_s, b2s_prio_statistic_r) = mpsc::unbounded::<(Pid, u64, u64)>(); + let (a2s_disconnect_s, a2s_disconnect_r) = mpsc::unbounded::(); + let (b2s_prio_statistic_s, b2s_prio_statistic_r) = mpsc::unbounded::(); let run_channels = Some(ControlChannels { a2s_listen_r, @@ -155,10 +153,7 @@ impl Scheduler { ); } - async fn listen_mgr( - &self, - a2s_listen_r: mpsc::UnboundedReceiver<(ProtocolAddr, oneshot::Sender>)>, - ) { + async fn listen_mgr(&self, a2s_listen_r: mpsc::UnboundedReceiver) { trace!("Start listen_mgr"); a2s_listen_r .for_each_concurrent(None, |(address, s2a_listen_result_s)| { @@ -253,13 +248,7 @@ impl Scheduler { trace!("Stop connect_mgr"); } - async fn disconnect_mgr( - &self, - mut a2s_disconnect_r: mpsc::UnboundedReceiver<( - Pid, - oneshot::Sender>, - )>, - ) { + async fn disconnect_mgr(&self, mut a2s_disconnect_r: mpsc::UnboundedReceiver) { trace!("Start disconnect_mgr"); while let Some((pid, return_once_successful_shutdown)) = a2s_disconnect_r.next().await { //Closing Participants is done the following way: @@ -289,7 +278,7 @@ impl Scheduler { async fn prio_adj_mgr( &self, - mut b2s_prio_statistic_r: mpsc::UnboundedReceiver<(Pid, u64, u64)>, + mut b2s_prio_statistic_r: mpsc::UnboundedReceiver, ) { trace!("Start prio_adj_mgr"); while let Some((_pid, _frame_cnt, _unused)) = b2s_prio_statistic_r.next().await { @@ -512,7 +501,7 @@ impl Scheduler { debug!(?cid, "New participant connected via a channel"); let ( bparticipant, - a2b_steam_open_s, + a2b_stream_open_s, b2a_stream_opened_r, mut s2b_create_channel_s, s2b_shutdown_bparticipant_s, @@ -522,7 +511,7 @@ impl Scheduler { let participant = Participant::new( local_pid, pid, - a2b_steam_open_s, + a2b_stream_open_s, b2a_stream_opened_r, participant_channels.a2s_disconnect_s, api_participant_closed, From 6c59caf8e19c52b53e09541e3fdfed1f5baf33ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=A4rtens?= Date: Wed, 15 Jul 2020 01:34:41 +0200 Subject: [PATCH 34/85] make `prometheus` optional in network and fix a panic in the server - an extra interface `new_with_regisitry` was created to make sure the interface doesn't depend on the features --- client/src/lib.rs | 2 +- network/Cargo.toml | 8 +- network/examples/chat/src/main.rs | 4 +- network/examples/fileshare/src/server.rs | 2 +- network/examples/network-speed/src/main.rs | 4 +- network/src/api.rs | 97 ++++++++---- network/src/channel.rs | 174 ++++++++++----------- network/src/lib.rs | 6 +- network/src/participant.rs | 57 +++++-- network/src/prios.rs | 41 +++-- network/src/protocols.rs | 43 ++++- network/src/scheduler.rs | 44 +++++- network/src/types.rs | 6 +- network/tests/helper.rs | 4 +- network/tests/integration.rs | 16 +- server/Cargo.toml | 2 +- server/src/lib.rs | 15 +- 17 files changed, 336 insertions(+), 189 deletions(-) diff --git a/client/src/lib.rs b/client/src/lib.rs index 482739defe..1603618a92 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -119,7 +119,7 @@ impl Client { // We reduce the thread count by 1 to keep rendering smooth thread_pool.set_num_threads((num_cpus::get() - 1).max(1)); - let (network, f) = Network::new(Pid::new(), None); + let (network, f) = Network::new(Pid::new()); thread_pool.execute(f); let participant = block_on(network.connect(ProtocolAddr::Tcp(addr.into())))?; diff --git a/network/Cargo.toml b/network/Cargo.toml index 72bdde9ebc..d6c271ee48 100644 --- a/network/Cargo.toml +++ b/network/Cargo.toml @@ -6,6 +6,12 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[features] +metrics = ["prometheus"] + +default = ["metrics"] + [dependencies] lz4-compress = "0.1.1" @@ -19,7 +25,7 @@ async-std = { version = "~1.5", default-features = false, features = ["std", "as #tracing and metrics tracing = { version = "0.1", default-features = false } tracing-futures = "0.2" -prometheus = { version = "0.9", default-features = false } +prometheus = { version = "0.9", default-features = false, optional = true } #async futures = { version = "0.3", features = ["thread-pool"] } #mpsc channel registry diff --git a/network/examples/chat/src/main.rs b/network/examples/chat/src/main.rs index f8b93b6581..89ca24c054 100644 --- a/network/examples/chat/src/main.rs +++ b/network/examples/chat/src/main.rs @@ -100,7 +100,7 @@ fn main() { } fn server(address: ProtocolAddr) { - let (server, f) = Network::new(Pid::new(), None); + let (server, f) = Network::new(Pid::new()); let server = Arc::new(server); std::thread::spawn(f); let pool = ThreadPool::new().unwrap(); @@ -144,7 +144,7 @@ async fn client_connection(_network: Arc, participant: Arc } fn client(address: ProtocolAddr) { - let (client, f) = Network::new(Pid::new(), None); + let (client, f) = Network::new(Pid::new()); std::thread::spawn(f); let pool = ThreadPool::new().unwrap(); diff --git a/network/examples/fileshare/src/server.rs b/network/examples/fileshare/src/server.rs index 50df21e65e..a94a1e668c 100644 --- a/network/examples/fileshare/src/server.rs +++ b/network/examples/fileshare/src/server.rs @@ -26,7 +26,7 @@ impl Server { pub fn new() -> (Self, mpsc::UnboundedSender) { let (command_sender, command_receiver) = mpsc::unbounded(); - let (network, f) = Network::new(Pid::new(), None); + let (network, f) = Network::new(Pid::new()); std::thread::spawn(f); let run_channels = Some(ControlChannels { command_receiver }); diff --git a/network/examples/network-speed/src/main.rs b/network/examples/network-speed/src/main.rs index 91072d1153..f182b84483 100644 --- a/network/examples/network-speed/src/main.rs +++ b/network/examples/network-speed/src/main.rs @@ -120,7 +120,7 @@ fn main() { fn server(address: ProtocolAddr) { let mut metrics = metrics::SimpleMetrics::new(); - let (server, f) = Network::new(Pid::new(), Some(metrics.registry())); + let (server, f) = Network::new_with_registry(Pid::new(), metrics.registry()); std::thread::spawn(f); metrics.run("0.0.0.0:59112".parse().unwrap()).unwrap(); block_on(server.listen(address)).unwrap(); @@ -148,7 +148,7 @@ fn server(address: ProtocolAddr) { fn client(address: ProtocolAddr) { let mut metrics = metrics::SimpleMetrics::new(); - let (client, f) = Network::new(Pid::new(), Some(metrics.registry())); + let (client, f) = Network::new_with_registry(Pid::new(), metrics.registry()); std::thread::spawn(f); metrics.run("0.0.0.0:59111".parse().unwrap()).unwrap(); diff --git a/network/src/api.rs b/network/src/api.rs index 39b47ad1ce..33ce2de6ad 100644 --- a/network/src/api.rs +++ b/network/src/api.rs @@ -18,6 +18,7 @@ use futures::{ sink::SinkExt, stream::StreamExt, }; +#[cfg(feature = "metrics")] use prometheus::Registry; use serde::{de::DeserializeOwned, Serialize}; use std::{ @@ -127,11 +128,11 @@ pub enum StreamError { /// /// # fn main() -> std::result::Result<(), Box> { /// // Create a Network, listen on port `2999` to accept connections and connect to port `8080` to connect to a (pseudo) database Application -/// let (network, f) = Network::new(Pid::new(), None); +/// let (network, f) = Network::new(Pid::new()); /// std::thread::spawn(f); /// block_on(async{ /// # //setup pseudo database! -/// # let (database, fd) = Network::new(Pid::new(), None); +/// # let (database, fd) = Network::new(Pid::new()); /// # std::thread::spawn(fd); /// # database.listen(ProtocolAddr::Tcp("127.0.0.1:8080".parse().unwrap())).await?; /// network.listen(ProtocolAddr::Tcp("127.0.0.1:2999".parse().unwrap())).await?; @@ -162,9 +163,6 @@ impl Network { /// # Arguments /// * `participant_id` - provide it by calling [`Pid::new()`], usually you /// don't want to reuse a Pid for 2 `Networks` - /// * `registry` - Provide a Registy in order to collect Prometheus metrics - /// by this `Network`, `None` will deactivate Tracing. Tracing is done via - /// [`prometheus`] /// /// # Result /// * `Self` - returns a `Network` which can be `Send` to multiple areas of @@ -184,7 +182,7 @@ impl Network { /// use veloren_network::{Network, Pid, ProtocolAddr}; /// /// let pool = ThreadPoolBuilder::new().build(); - /// let (network, f) = Network::new(Pid::new(), None); + /// let (network, f) = Network::new(Pid::new()); /// pool.execute(f); /// ``` /// @@ -192,11 +190,11 @@ impl Network { /// //Example with std::thread /// use veloren_network::{Network, Pid, ProtocolAddr}; /// - /// let (network, f) = Network::new(Pid::new(), None); + /// let (network, f) = Network::new(Pid::new()); /// std::thread::spawn(f); /// ``` /// - /// Usually you only create a single `Network` for an appliregistrycation, + /// Usually you only create a single `Network` for an application, /// except when client and server are in the same application, then you /// will want 2. However there are no technical limitations from /// creating more. @@ -204,14 +202,51 @@ impl Network { /// [`Pid::new()`]: crate::types::Pid::new /// [`ThreadPool`]: https://docs.rs/uvth/newest/uvth/struct.ThreadPool.html /// [`uvth`]: https://docs.rs/uvth - pub fn new( + pub fn new(participant_id: Pid) -> (Self, impl std::ops::FnOnce()) { + Self::internal_new( + participant_id, + #[cfg(feature = "metrics")] + None, + ) + } + + /// See [`new`] + /// + /// # additional Arguments + /// * `registry` - Provide a Registy in order to collect Prometheus metrics + /// by this `Network`, `None` will deactivate Tracing. Tracing is done via + /// [`prometheus`] + /// + /// # Examples + /// ```rust + /// use prometheus::Registry; + /// use veloren_network::{Network, Pid, ProtocolAddr}; + /// + /// let registry = Registry::new(); + /// let (network, f) = Network::new_with_registry(Pid::new(), ®istry); + /// std::thread::spawn(f); + /// ``` + /// [`new`]: crate::api::Network::new + #[cfg(feature = "metrics")] + pub fn new_with_registry( participant_id: Pid, - registry: Option<&Registry>, + registry: &Registry, + ) -> (Self, impl std::ops::FnOnce()) { + Self::internal_new(participant_id, Some(registry)) + } + + fn internal_new( + participant_id: Pid, + #[cfg(feature = "metrics")] registry: Option<&Registry>, ) -> (Self, impl std::ops::FnOnce()) { let p = participant_id; debug!(?p, "Starting Network"); let (scheduler, listen_sender, connect_sender, connected_receiver, shutdown_sender) = - Scheduler::new(participant_id, registry); + Scheduler::new( + participant_id, + #[cfg(feature = "metrics")] + registry, + ); ( Self { local_pid: participant_id, @@ -222,13 +257,13 @@ impl Network { shutdown_sender: Some(shutdown_sender), }, move || { - trace!(?p, "Starting sheduler in own thread"); + trace!(?p, "Starting scheduler in own thread"); let _handle = task::block_on( scheduler .run() .instrument(tracing::info_span!("scheduler", ?p)), ); - trace!(?p, "Stopping sheduler and his own thread"); + trace!(?p, "Stopping scheduler and his own thread"); }, ) } @@ -247,7 +282,7 @@ impl Network { /// /// # fn main() -> std::result::Result<(), Box> { /// // Create a Network, listen on port `2000` TCP on all NICs and `2001` UDP locally - /// let (network, f) = Network::new(Pid::new(), None); + /// let (network, f) = Network::new(Pid::new()); /// std::thread::spawn(f); /// block_on(async { /// network @@ -288,9 +323,9 @@ impl Network { /// /// # fn main() -> std::result::Result<(), Box> { /// // Create a Network, connect on port `2010` TCP and `2011` UDP like listening above - /// let (network, f) = Network::new(Pid::new(), None); + /// let (network, f) = Network::new(Pid::new()); /// std::thread::spawn(f); - /// # let (remote, fr) = Network::new(Pid::new(), None); + /// # let (remote, fr) = Network::new(Pid::new()); /// # std::thread::spawn(fr); /// block_on(async { /// # remote.listen(ProtocolAddr::Tcp("0.0.0.0:2010".parse().unwrap())).await?; @@ -354,9 +389,9 @@ impl Network { /// /// # fn main() -> std::result::Result<(), Box> { /// // Create a Network, listen on port `2020` TCP and opens returns their Pid - /// let (network, f) = Network::new(Pid::new(), None); + /// let (network, f) = Network::new(Pid::new()); /// std::thread::spawn(f); - /// # let (remote, fr) = Network::new(Pid::new(), None); + /// # let (remote, fr) = Network::new(Pid::new()); /// # std::thread::spawn(fr); /// block_on(async { /// network @@ -428,9 +463,9 @@ impl Participant { /// /// # fn main() -> std::result::Result<(), Box> { /// // Create a Network, connect on port 2100 and open a stream - /// let (network, f) = Network::new(Pid::new(), None); + /// let (network, f) = Network::new(Pid::new()); /// std::thread::spawn(f); - /// # let (remote, fr) = Network::new(Pid::new(), None); + /// # let (remote, fr) = Network::new(Pid::new()); /// # std::thread::spawn(fr); /// block_on(async { /// # remote.listen(ProtocolAddr::Tcp("0.0.0.0:2100".parse().unwrap())).await?; @@ -483,9 +518,9 @@ impl Participant { /// # fn main() -> std::result::Result<(), Box> { /// // Create a Network, connect on port 2110 and wait for the other side to open a stream /// // Note: It's quite unusal to activly connect, but then wait on a stream to be connected, usually the Appication taking initiative want's to also create the first Stream. - /// let (network, f) = Network::new(Pid::new(), None); + /// let (network, f) = Network::new(Pid::new()); /// std::thread::spawn(f); - /// # let (remote, fr) = Network::new(Pid::new(), None); + /// # let (remote, fr) = Network::new(Pid::new()); /// # std::thread::spawn(fr); /// block_on(async { /// # remote.listen(ProtocolAddr::Tcp("0.0.0.0:2110".parse().unwrap())).await?; @@ -542,9 +577,9 @@ impl Participant { /// /// # fn main() -> std::result::Result<(), Box> { /// // Create a Network, listen on port `2030` TCP and opens returns their Pid and close connection. - /// let (network, f) = Network::new(Pid::new(), None); + /// let (network, f) = Network::new(Pid::new()); /// std::thread::spawn(f); - /// # let (remote, fr) = Network::new(Pid::new(), None); + /// # let (remote, fr) = Network::new(Pid::new()); /// # std::thread::spawn(fr); /// block_on(async { /// network @@ -679,9 +714,9 @@ impl Stream { /// /// # fn main() -> std::result::Result<(), Box> { /// // Create a Network, listen on Port `2200` and wait for a Stream to be opened, then answer `Hello World` - /// let (network, f) = Network::new(Pid::new(), None); + /// let (network, f) = Network::new(Pid::new()); /// std::thread::spawn(f); - /// # let (remote, fr) = Network::new(Pid::new(), None); + /// # let (remote, fr) = Network::new(Pid::new()); /// # std::thread::spawn(fr); /// block_on(async { /// network.listen(ProtocolAddr::Tcp("127.0.0.1:2200".parse().unwrap())).await?; @@ -719,11 +754,11 @@ impl Stream { /// use std::sync::Arc; /// /// # fn main() -> std::result::Result<(), Box> { - /// let (network, f) = Network::new(Pid::new(), None); + /// let (network, f) = Network::new(Pid::new()); /// std::thread::spawn(f); - /// # let (remote1, fr1) = Network::new(Pid::new(), None); + /// # let (remote1, fr1) = Network::new(Pid::new()); /// # std::thread::spawn(fr1); - /// # let (remote2, fr2) = Network::new(Pid::new(), None); + /// # let (remote2, fr2) = Network::new(Pid::new()); /// # std::thread::spawn(fr2); /// block_on(async { /// network.listen(ProtocolAddr::Tcp("127.0.0.1:2210".parse().unwrap())).await?; @@ -784,9 +819,9 @@ impl Stream { /// /// # fn main() -> std::result::Result<(), Box> { /// // Create a Network, listen on Port `2220` and wait for a Stream to be opened, then listen on it - /// let (network, f) = Network::new(Pid::new(), None); + /// let (network, f) = Network::new(Pid::new()); /// std::thread::spawn(f); - /// # let (remote, fr) = Network::new(Pid::new(), None); + /// # let (remote, fr) = Network::new(Pid::new()); /// # std::thread::spawn(fr); /// block_on(async { /// network.listen(ProtocolAddr::Tcp("127.0.0.1:2220".parse().unwrap())).await?; diff --git a/network/src/channel.rs b/network/src/channel.rs index 752dad16cb..25babccd2b 100644 --- a/network/src/channel.rs +++ b/network/src/channel.rs @@ -1,5 +1,6 @@ +#[cfg(feature = "metrics")] +use crate::metrics::NetworkMetrics; use crate::{ - metrics::NetworkMetrics, protocols::Protocols, types::{ Cid, Frame, Pid, Sid, STREAM_ID_OFFSET1, STREAM_ID_OFFSET2, VELOREN_MAGIC_NUMBER, @@ -13,7 +14,7 @@ use futures::{ stream::StreamExt, FutureExt, }; -use std::sync::Arc; +#[cfg(feature = "metrics")] use std::sync::Arc; use tracing::*; pub(crate) struct Channel { @@ -80,6 +81,7 @@ pub(crate) struct Handshake { local_pid: Pid, secret: u128, init_handshake: bool, + #[cfg(feature = "metrics")] metrics: Arc, } @@ -98,13 +100,14 @@ impl Handshake { cid: u64, local_pid: Pid, secret: u128, - metrics: Arc, + #[cfg(feature = "metrics")] metrics: Arc, init_handshake: bool, ) -> Self { Self { cid, local_pid, secret, + #[cfg(feature = "metrics")] metrics, init_handshake, } @@ -163,104 +166,73 @@ impl Handshake { ) -> Result<(Pid, Sid, u128), ()> { const ERR_S: &str = "Got A Raw Message, these are usually Debug Messages indicating that \ something went wrong on network layer and connection will be closed"; - let mut pid_string = "".to_string(); + #[cfg(feature = "metrics")] let cid_string = self.cid.to_string(); if self.init_handshake { self.send_handshake(&mut c2w_frame_s).await; } - let r = match w2c_cid_frame_r.next().await { - Some(( - _, - Frame::Handshake { - magic_number, - version, - }, - )) => { - trace!(?magic_number, ?version, "Recv handshake"); + let frame = w2c_cid_frame_r.next().await.map(|(_cid, frame)| frame); + #[cfg(feature = "metrics")] + { + if let Some(ref frame) = frame { self.metrics .frames_in_total - .with_label_values(&["", &cid_string, "Handshake"]) + .with_label_values(&["", &cid_string, &frame.get_string()]) .inc(); + } + } + let r = match frame { + Some(Frame::Handshake { + magic_number, + version, + }) => { + trace!(?magic_number, ?version, "Recv handshake"); if magic_number != VELOREN_MAGIC_NUMBER { error!(?magic_number, "Connection with invalid magic_number"); #[cfg(debug_assertions)] - { - self.metrics - .frames_out_total - .with_label_values(&["", &cid_string, "Raw"]) - .inc(); - debug!("Sending client instructions before killing"); - c2w_frame_s - .send(Frame::Raw(Self::WRONG_NUMBER.to_vec())) - .await - .unwrap(); - c2w_frame_s.send(Frame::Shutdown).await.unwrap(); - } + self.send_raw_and_shutdown(&mut c2w_frame_s, Self::WRONG_NUMBER.to_vec()) + .await; Err(()) } else if version != VELOREN_NETWORK_VERSION { error!(?version, "Connection with wrong network version"); #[cfg(debug_assertions)] - { - debug!("Sending client instructions before killing"); - self.metrics - .frames_out_total - .with_label_values(&["", &cid_string, "Raw"]) - .inc(); - c2w_frame_s - .send(Frame::Raw( - format!( - "{} Our Version: {:?}\nYour Version: {:?}\nClosing the \ - connection", - Self::WRONG_VERSION, - VELOREN_NETWORK_VERSION, - version, - ) - .as_bytes() - .to_vec(), - )) - .await - .unwrap(); - c2w_frame_s.send(Frame::Shutdown {}).await.unwrap(); - } + self.send_raw_and_shutdown( + &mut c2w_frame_s, + format!( + "{} Our Version: {:?}\nYour Version: {:?}\nClosing the connection", + Self::WRONG_VERSION, + VELOREN_NETWORK_VERSION, + version, + ) + .as_bytes() + .to_vec(), + ) + .await; Err(()) } else { debug!("Handshake completed"); if self.init_handshake { - self.send_init(&mut c2w_frame_s, &pid_string).await; + self.send_init(&mut c2w_frame_s, "").await; } else { self.send_handshake(&mut c2w_frame_s).await; } Ok(()) } }, - Some((_, Frame::Shutdown)) => { + Some(Frame::Shutdown) => { info!("Shutdown signal received"); - self.metrics - .frames_in_total - .with_label_values(&[&pid_string, &cid_string, "Shutdown"]) - .inc(); Err(()) }, - Some((_, Frame::Raw(bytes))) => { - self.metrics - .frames_in_total - .with_label_values(&[&pid_string, &cid_string, "Raw"]) - .inc(); + Some(Frame::Raw(bytes)) => { match std::str::from_utf8(bytes.as_slice()) { Ok(string) => error!(?string, ERR_S), _ => error!(?bytes, ERR_S), } Err(()) }, - Some((_, frame)) => { - self.metrics - .frames_in_total - .with_label_values(&[&pid_string, &cid_string, frame.get_string()]) - .inc(); - Err(()) - }, + Some(_) => Err(()), None => Err(()), }; if let Err(()) = r { @@ -274,10 +246,12 @@ impl Handshake { return Err(()); } - let r = match w2c_cid_frame_r.next().await { - Some((_, Frame::Init { pid, secret })) => { + let frame = w2c_cid_frame_r.next().await.map(|(_cid, frame)| frame); + let r = match frame { + Some(Frame::Init { pid, secret }) => { debug!(?pid, "Participant send their ID"); - pid_string = pid.to_string(); + let pid_string = pid.to_string(); + #[cfg(feature = "metrics")] self.metrics .frames_in_total .with_label_values(&[&pid_string, &cid_string, "ParticipantId"]) @@ -291,32 +265,22 @@ impl Handshake { info!(?pid, "This Handshake is now configured!"); Ok((pid, stream_id_offset, secret)) }, - Some((_, Frame::Shutdown)) => { - info!("Shutdown signal received"); + Some(frame) => { + #[cfg(feature = "metrics")] self.metrics .frames_in_total - .with_label_values(&[&pid_string, &cid_string, "Shutdown"]) + .with_label_values(&["", &cid_string, frame.get_string()]) .inc(); - Err(()) - }, - Some((_, Frame::Raw(bytes))) => { - self.metrics - .frames_in_total - .with_label_values(&[&pid_string, &cid_string, "Raw"]) - .inc(); - match std::str::from_utf8(bytes.as_slice()) { - Ok(string) => error!(?string, ERR_S), - _ => error!(?bytes, ERR_S), + match frame { + Frame::Shutdown => info!("Shutdown signal received"), + Frame::Raw(bytes) => match std::str::from_utf8(bytes.as_slice()) { + Ok(string) => error!(?string, ERR_S), + _ => error!(?bytes, ERR_S), + }, + _ => (), } Err(()) }, - Some((_, frame)) => { - self.metrics - .frames_in_total - .with_label_values(&[&pid_string, &cid_string, frame.get_string()]) - .inc(); - Err(()) - }, None => Err(()), }; if r.is_err() { @@ -332,6 +296,7 @@ impl Handshake { } async fn send_handshake(&self, c2w_frame_s: &mut mpsc::UnboundedSender) { + #[cfg(feature = "metrics")] self.metrics .frames_out_total .with_label_values(&["", &self.cid.to_string(), "Handshake"]) @@ -345,7 +310,13 @@ impl Handshake { .unwrap(); } - async fn send_init(&self, c2w_frame_s: &mut mpsc::UnboundedSender, pid_string: &str) { + async fn send_init( + &self, + c2w_frame_s: &mut mpsc::UnboundedSender, + #[cfg(feature = "metrics")] pid_string: &str, + #[cfg(not(feature = "metrics"))] _pid_string: &str, + ) { + #[cfg(feature = "metrics")] self.metrics .frames_out_total .with_label_values(&[pid_string, &self.cid.to_string(), "ParticipantId"]) @@ -358,4 +329,27 @@ impl Handshake { .await .unwrap(); } + + #[cfg(debug_assertions)] + async fn send_raw_and_shutdown( + &self, + c2w_frame_s: &mut mpsc::UnboundedSender, + data: Vec, + ) { + debug!("Sending client instructions before killing"); + #[cfg(feature = "metrics")] + { + let cid_string = self.cid.to_string(); + self.metrics + .frames_out_total + .with_label_values(&["", &cid_string, "Raw"]) + .inc(); + self.metrics + .frames_out_total + .with_label_values(&["", &cid_string, "Shutdown"]) + .inc(); + } + c2w_frame_s.send(Frame::Raw(data)).await.unwrap(); + c2w_frame_s.send(Frame::Shutdown).await.unwrap(); + } } diff --git a/network/src/lib.rs b/network/src/lib.rs index 7df6ffe8b6..51d4e7a1e1 100644 --- a/network/src/lib.rs +++ b/network/src/lib.rs @@ -45,7 +45,7 @@ //! // Client //! async fn client() -> std::result::Result<(), Box> { //! sleep(std::time::Duration::from_secs(1)).await; // `connect` MUST be after `listen` -//! let (client_network, f) = Network::new(Pid::new(), None); +//! let (client_network, f) = Network::new(Pid::new()); //! std::thread::spawn(f); //! let server = client_network //! .connect(ProtocolAddr::Tcp("127.0.0.1:12345".parse().unwrap())) @@ -59,7 +59,7 @@ //! //! // Server //! async fn server() -> std::result::Result<(), Box> { -//! let (server_network, f) = Network::new(Pid::new(), None); +//! let (server_network, f) = Network::new(Pid::new()); //! std::thread::spawn(f); //! server_network //! .listen(ProtocolAddr::Tcp("127.0.0.1:12345".parse().unwrap())) @@ -101,7 +101,7 @@ mod api; mod channel; mod message; -mod metrics; +#[cfg(feature = "metrics")] mod metrics; mod participant; mod prios; mod protocols; diff --git a/network/src/participant.rs b/network/src/participant.rs index 901b10b4c7..8d6fa66aab 100644 --- a/network/src/participant.rs +++ b/network/src/participant.rs @@ -1,8 +1,9 @@ +#[cfg(feature = "metrics")] +use crate::metrics::{NetworkMetrics, PidCidFrameCache}; use crate::{ api::{ParticipantError, Stream}, channel::Channel, message::{IncomingMessage, MessageBuffer, OutgoingMessage}, - metrics::{NetworkMetrics, PidCidFrameCache}, prios::PrioManager, protocols::Protocols, types::{Cid, Frame, Pid, Prio, Promises, Sid}, @@ -66,6 +67,7 @@ pub struct BParticipant { api_participant_closed: Arc>>, running_mgr: AtomicUsize, run_channels: Option, + #[cfg(feature = "metrics")] metrics: Arc, no_channel_error_info: RwLock<(Instant, u64)>, } @@ -75,7 +77,7 @@ impl BParticipant { pub(crate) fn new( remote_pid: Pid, offset_sid: Sid, - metrics: Arc, + #[cfg(feature = "metrics")] metrics: Arc, ) -> ( Self, mpsc::UnboundedSender, @@ -111,6 +113,7 @@ impl BParticipant { api_participant_closed: api_participant_closed.clone(), running_mgr: AtomicUsize::new(0), run_channels, + #[cfg(feature = "metrics")] metrics, no_channel_error_info: RwLock::new((Instant::now(), 0)), }, @@ -131,8 +134,11 @@ impl BParticipant { let (shutdown_open_mgr_sender, shutdown_open_mgr_receiver) = oneshot::channel(); let (b2b_prios_flushed_s, b2b_prios_flushed_r) = oneshot::channel(); let (w2b_frames_s, w2b_frames_r) = mpsc::unbounded::<(Cid, Frame)>(); - let (prios, a2p_msg_s, b2p_notify_empty_stream_s) = - PrioManager::new(self.metrics.clone(), self.remote_pid_string.clone()); + let (prios, a2p_msg_s, b2p_notify_empty_stream_s) = PrioManager::new( + #[cfg(feature = "metrics")] + self.metrics.clone(), + self.remote_pid_string.clone(), + ); let run_channels = self.run_channels.take().unwrap(); futures::join!( @@ -187,6 +193,7 @@ impl BParticipant { self.running_mgr.fetch_add(1, Ordering::Relaxed); let mut closing_up = false; trace!("Start send_mgr"); + #[cfg(feature = "metrics")] let mut send_cache = PidCidFrameCache::new(self.metrics.frames_out_total.clone(), self.remote_pid); loop { @@ -197,7 +204,12 @@ impl BParticipant { trace!("Tick {}", len); } for (_, frame) in frames { - self.send_frame(frame, &mut send_cache).await; + self.send_frame( + frame, + #[cfg(feature = "metrics")] + &mut send_cache, + ) + .await; } b2s_prio_statistic_s .send((self.remote_pid, len as u64, /* */ 0)) @@ -225,7 +237,7 @@ impl BParticipant { async fn send_frame( &self, frame: Frame, - frames_out_total_cache: &mut PidCidFrameCache, + #[cfg(feature = "metrics")] frames_out_total_cache: &mut PidCidFrameCache, ) -> bool { // find out ideal channel here //TODO: just take first @@ -234,6 +246,7 @@ impl BParticipant { //note: this is technically wrong we should only increase when it suceeded, but // this requiered me to clone `frame` which is a to big performance impact for // error handling + #[cfg(feature = "metrics")] frames_out_total_cache .with_label_values(ci.cid, &frame) .inc(); @@ -292,12 +305,17 @@ impl BParticipant { let mut dropped_sid = Sid::new(0); while let Some((cid, frame)) = w2b_frames_r.next().await { - let cid_string = cid.to_string(); //trace!("handling frame"); - self.metrics - .frames_in_total - .with_label_values(&[&self.remote_pid_string, &cid_string, frame.get_string()]) - .inc(); + #[cfg(feature = "metrics")] + { + let cid_string = cid.to_string(); + self.metrics + .frames_in_total + .with_label_values(&[&self.remote_pid_string, &cid_string, frame.get_string()]) + .inc(); + } + #[cfg(not(feature = "metrics"))] + let _cid = cid; match frame { Frame::OpenStream { sid, @@ -324,6 +342,7 @@ impl BParticipant { ); // no wait for flush here, as the remote wouldn't care anyway. if let Some(si) = self.streams.write().await.remove(&sid) { + #[cfg(feature = "metrics")] self.metrics .streams_closed_total .with_label_values(&[&self.remote_pid_string]) @@ -433,6 +452,7 @@ impl BParticipant { b2r_read_shutdown, }); b2s_create_channel_done_s.send(()).unwrap(); + #[cfg(feature = "metrics")] self.metrics .channels_connected_total .with_label_values(&[&self.remote_pid_string]) @@ -441,6 +461,7 @@ impl BParticipant { channel .run(protocol, w2b_frames_s, leftover_cid_frame) .await; + #[cfg(feature = "metrics")] self.metrics .channels_disconnected_total .with_label_values(&[&self.remote_pid_string]) @@ -464,6 +485,7 @@ impl BParticipant { self.running_mgr.fetch_add(1, Ordering::Relaxed); trace!("Start open_mgr"); let mut stream_ids = self.offset_sid; + #[cfg(feature = "metrics")] let mut send_cache = PidCidFrameCache::new(self.metrics.frames_out_total.clone(), self.remote_pid); let mut shutdown_open_mgr_receiver = shutdown_open_mgr_receiver.fuse(); @@ -485,6 +507,7 @@ impl BParticipant { prio, promises, }, + #[cfg(feature = "metrics")] &mut send_cache, ) .await @@ -553,6 +576,7 @@ impl BParticipant { } trace!("All BParticipant mgr (except me) are shut down now"); + #[cfg(feature = "metrics")] self.metrics.participants_disconnected_total.inc(); debug!("BParticipant close done"); @@ -569,6 +593,7 @@ impl BParticipant { ) { self.running_mgr.fetch_add(1, Ordering::Relaxed); trace!("Start stream_close_mgr"); + #[cfg(feature = "metrics")] let mut send_cache = PidCidFrameCache::new(self.metrics.frames_out_total.clone(), self.remote_pid); let mut shutdown_stream_close_mgr_receiver = shutdown_stream_close_mgr_receiver.fuse(); @@ -606,14 +631,19 @@ impl BParticipant { s2b_stream_finished_closed_r.await.unwrap(); trace!(?sid, "Stream was successfully flushed"); + #[cfg(feature = "metrics")] self.metrics .streams_closed_total .with_label_values(&[&self.remote_pid_string]) .inc(); //only now remove the Stream, that means we can still recv on it. self.streams.write().await.remove(&sid); - self.send_frame(Frame::CloseStream { sid }, &mut send_cache) - .await; + self.send_frame( + Frame::CloseStream { sid }, + #[cfg(feature = "metrics")] + &mut send_cache, + ) + .await; } trace!("Stop stream_close_mgr"); self.running_mgr.fetch_sub(1, Ordering::Relaxed); @@ -635,6 +665,7 @@ impl BParticipant { b2a_msg_recv_s, closed: closed.clone(), }); + #[cfg(feature = "metrics")] self.metrics .streams_opened_total .with_label_values(&[&self.remote_pid_string]) diff --git a/network/src/prios.rs b/network/src/prios.rs index 83905a06dd..4e5971a32e 100644 --- a/network/src/prios.rs +++ b/network/src/prios.rs @@ -4,18 +4,17 @@ //!E.g. in the same time 100 prio0 messages are send, only 50 prio5, 25 prio10, //! 12 prio15 or 6 prio20 messages are send. Note: TODO: prio0 will be send //! immeadiatly when found! - +//! +#[cfg(feature = "metrics")] +use crate::metrics::NetworkMetrics; use crate::{ message::OutgoingMessage, - metrics::NetworkMetrics, types::{Frame, Prio, Sid}, }; use crossbeam_channel::{unbounded, Receiver, Sender}; use futures::channel::oneshot; -use std::{ - collections::{HashMap, HashSet, VecDeque}, - sync::Arc, -}; +use std::collections::{HashMap, HashSet, VecDeque}; +#[cfg(feature = "metrics")] use std::sync::Arc; use tracing::*; @@ -35,7 +34,9 @@ pub(crate) struct PrioManager { //you can register to be notified if a pid_sid combination is flushed completly here sid_flushed_rx: Receiver<(Sid, oneshot::Sender<()>)>, queued: HashSet, + #[cfg(feature = "metrics")] metrics: Arc, + #[cfg(feature = "metrics")] pid: String, } @@ -50,13 +51,15 @@ impl PrioManager { #[allow(clippy::type_complexity)] pub fn new( - metrics: Arc, + #[cfg(feature = "metrics")] metrics: Arc, pid: String, ) -> ( Self, Sender<(Prio, Sid, OutgoingMessage)>, Sender<(Sid, oneshot::Sender<()>)>, ) { + #[cfg(not(feature = "metrics"))] + let _pid = pid; // (a2p_msg_s, a2p_msg_r) let (messages_tx, messages_rx) = unbounded(); let (sid_flushed_tx, sid_flushed_rx) = unbounded(); @@ -133,7 +136,9 @@ impl PrioManager { queued: HashSet::new(), //TODO: optimize with u64 and 64 bits sid_flushed_rx, sid_owned: HashMap::new(), + #[cfg(feature = "metrics")] metrics, + #[cfg(feature = "metrics")] pid, }, messages_tx, @@ -148,15 +153,19 @@ impl PrioManager { for (prio, sid, msg) in self.messages_rx.try_iter() { debug_assert!(prio as usize <= PRIO_MAX); messages += 1; - let sid_string = sid.to_string(); - self.metrics - .message_out_total - .with_label_values(&[&self.pid, &sid_string]) - .inc(); - self.metrics - .message_out_throughput - .with_label_values(&[&self.pid, &sid_string]) - .inc_by(msg.buffer.data.len() as i64); + #[cfg(feature = "metrics")] + { + let sid_string = sid.to_string(); + self.metrics + .message_out_total + .with_label_values(&[&self.pid, &sid_string]) + .inc(); + self.metrics + .message_out_throughput + .with_label_values(&[&self.pid, &sid_string]) + .inc_by(msg.buffer.data.len() as i64); + } + //trace!(?prio, ?sid_string, "tick"); self.queued.insert(prio); self.messages[prio as usize].push_back((sid, msg)); diff --git a/network/src/protocols.rs b/network/src/protocols.rs index 970867517f..687e1d8885 100644 --- a/network/src/protocols.rs +++ b/network/src/protocols.rs @@ -1,7 +1,6 @@ -use crate::{ - metrics::{CidFrameCache, NetworkMetrics}, - types::{Cid, Frame, Mid, Pid, Sid}, -}; +#[cfg(feature = "metrics")] +use crate::metrics::{CidFrameCache, NetworkMetrics}; +use crate::types::{Cid, Frame, Mid, Pid, Sid}; use async_std::{ net::{TcpStream, UdpSocket}, prelude::*, @@ -41,6 +40,7 @@ pub(crate) enum Protocols { #[derive(Debug)] pub(crate) struct TcpProtocol { stream: TcpStream, + #[cfg(feature = "metrics")] metrics: Arc, } @@ -48,14 +48,22 @@ pub(crate) struct TcpProtocol { pub(crate) struct UdpProtocol { socket: Arc, remote_addr: SocketAddr, + #[cfg(feature = "metrics")] metrics: Arc, data_in: Mutex>>, } //TODO: PERFORMACE: Use BufWriter and BufReader from std::io! impl TcpProtocol { - pub(crate) fn new(stream: TcpStream, metrics: Arc) -> Self { - Self { stream, metrics } + pub(crate) fn new( + stream: TcpStream, + #[cfg(feature = "metrics")] metrics: Arc, + ) -> Self { + Self { + stream, + #[cfg(feature = "metrics")] + metrics, + } } /// read_except and if it fails, close the protocol @@ -98,7 +106,9 @@ impl TcpProtocol { end_r: oneshot::Receiver<()>, ) { trace!("Starting up tcp read()"); + #[cfg(feature = "metrics")] let mut metrics_cache = CidFrameCache::new(self.metrics.frames_wire_in_total.clone(), cid); + #[cfg(feature = "metrics")] let throughput_cache = self .metrics .wire_in_throughput @@ -177,6 +187,7 @@ impl TcpProtocol { let start = u64::from_le_bytes(*<&[u8; 8]>::try_from(&bytes[8..16]).unwrap()); let length = u16::from_le_bytes(*<&[u8; 2]>::try_from(&bytes[16..18]).unwrap()); let mut data = vec![0; length as usize]; + #[cfg(feature = "metrics")] throughput_cache.inc_by(length as i64); read_or_close!(&mut data); Frame::Data { mid, start, data } @@ -199,6 +210,7 @@ impl TcpProtocol { Frame::Raw(data) }, }; + #[cfg(feature = "metrics")] metrics_cache.with_label_values(&frame).inc(); w2c_cid_frame_s .send((cid, frame)) @@ -230,11 +242,15 @@ impl TcpProtocol { pub async fn write_to_wire(&self, cid: Cid, mut c2w_frame_r: mpsc::UnboundedReceiver) { trace!("Starting up tcp write()"); let mut stream = self.stream.clone(); + #[cfg(feature = "metrics")] let mut metrics_cache = CidFrameCache::new(self.metrics.frames_wire_out_total.clone(), cid); + #[cfg(feature = "metrics")] let throughput_cache = self .metrics .wire_out_throughput .with_label_values(&[&cid.to_string()]); + #[cfg(not(feature = "metrics"))] + let _cid = cid; macro_rules! write_or_close { ($x:expr) => { @@ -246,6 +262,7 @@ impl TcpProtocol { } while let Some(frame) = c2w_frame_r.next().await { + #[cfg(feature = "metrics")] metrics_cache.with_label_values(&frame).inc(); match frame { Frame::Handshake { @@ -287,6 +304,7 @@ impl TcpProtocol { write_or_close!(&length.to_le_bytes()); }, Frame::Data { mid, start, data } => { + #[cfg(feature = "metrics")] throughput_cache.inc_by(data.len() as i64); write_or_close!(&FRAME_DATA.to_be_bytes()); write_or_close!(&mid.to_le_bytes()); @@ -309,12 +327,13 @@ impl UdpProtocol { pub(crate) fn new( socket: Arc, remote_addr: SocketAddr, - metrics: Arc, + #[cfg(feature = "metrics")] metrics: Arc, data_in: mpsc::UnboundedReceiver>, ) -> Self { Self { socket, remote_addr, + #[cfg(feature = "metrics")] metrics, data_in: Mutex::new(data_in), } @@ -327,7 +346,9 @@ impl UdpProtocol { end_r: oneshot::Receiver<()>, ) { trace!("Starting up udp read()"); + #[cfg(feature = "metrics")] let mut metrics_cache = CidFrameCache::new(self.metrics.frames_wire_in_total.clone(), cid); + #[cfg(feature = "metrics")] let throughput_cache = self .metrics .wire_in_throughput @@ -418,6 +439,7 @@ impl UdpProtocol { ]); let length = u16::from_le_bytes([bytes[17], bytes[18]]); let mut data = vec![0; length as usize]; + #[cfg(feature = "metrics")] throughput_cache.inc_by(length as i64); data.copy_from_slice(&bytes[19..]); Frame::Data { mid, start, data } @@ -430,6 +452,7 @@ impl UdpProtocol { }, _ => Frame::Raw(bytes), }; + #[cfg(feature = "metrics")] metrics_cache.with_label_values(&frame).inc(); w2c_cid_frame_s.send((cid, frame)).await.unwrap(); } @@ -439,12 +462,17 @@ impl UdpProtocol { pub async fn write_to_wire(&self, cid: Cid, mut c2w_frame_r: mpsc::UnboundedReceiver) { trace!("Starting up udp write()"); let mut buffer = [0u8; 2000]; + #[cfg(feature = "metrics")] let mut metrics_cache = CidFrameCache::new(self.metrics.frames_wire_out_total.clone(), cid); + #[cfg(feature = "metrics")] let throughput_cache = self .metrics .wire_out_throughput .with_label_values(&[&cid.to_string()]); + #[cfg(not(feature = "metrics"))] + let _cid = cid; while let Some(frame) = c2w_frame_r.next().await { + #[cfg(feature = "metrics")] metrics_cache.with_label_values(&frame).inc(); let len = match frame { Frame::Handshake { @@ -498,6 +526,7 @@ impl UdpProtocol { buffer[9..17].copy_from_slice(&start.to_le_bytes()); buffer[17..19].copy_from_slice(&(data.len() as u16).to_le_bytes()); buffer[19..(data.len() + 19)].clone_from_slice(&data[..]); + #[cfg(feature = "metrics")] throughput_cache.inc_by(data.len() as i64); 19 + data.len() }, diff --git a/network/src/scheduler.rs b/network/src/scheduler.rs index 931fabb2a1..b29818d1b1 100644 --- a/network/src/scheduler.rs +++ b/network/src/scheduler.rs @@ -1,7 +1,8 @@ +#[cfg(feature = "metrics")] +use crate::metrics::NetworkMetrics; use crate::{ api::{Participant, ProtocolAddr}, channel::Handshake, - metrics::NetworkMetrics, participant::{B2sPrioStatistic, BParticipant, S2bCreateChannel}, protocols::{Protocols, TcpProtocol, UdpProtocol}, types::Pid, @@ -18,6 +19,7 @@ use futures::{ sink::SinkExt, stream::StreamExt, }; +#[cfg(feature = "metrics")] use prometheus::Registry; use rand::Rng; use std::{ @@ -78,13 +80,14 @@ pub struct Scheduler { participants: Arc>>, channel_ids: Arc, channel_listener: RwLock>>, + #[cfg(feature = "metrics")] metrics: Arc, } impl Scheduler { pub fn new( local_pid: Pid, - registry: Option<&Registry>, + #[cfg(feature = "metrics")] registry: Option<&Registry>, ) -> ( Self, mpsc::UnboundedSender, @@ -113,9 +116,14 @@ impl Scheduler { b2s_prio_statistic_s, }; + #[cfg(feature = "metrics")] let metrics = Arc::new(NetworkMetrics::new(&local_pid).unwrap()); - if let Some(registry) = registry { - metrics.register(registry).unwrap(); + + #[cfg(feature = "metrics")] + { + if let Some(registry) = registry { + metrics.register(registry).unwrap(); + } } let mut rng = rand::thread_rng(); @@ -132,6 +140,7 @@ impl Scheduler { participants: Arc::new(RwLock::new(HashMap::new())), channel_ids: Arc::new(AtomicU64::new(0)), channel_listener: RwLock::new(HashMap::new()), + #[cfg(feature = "metrics")] metrics, }, a2s_listen_s, @@ -161,6 +170,7 @@ impl Scheduler { async move { debug!(?address, "Got request to open a channel_creator"); + #[cfg(feature = "metrics")] self.metrics .listen_requests_total .with_label_values(&[match address { @@ -193,6 +203,7 @@ impl Scheduler { while let Some((addr, pid_sender)) = a2s_connect_r.next().await { let (protocol, handshake) = match addr { ProtocolAddr::Tcp(addr) => { + #[cfg(feature = "metrics")] self.metrics .connect_requests_total .with_label_values(&["tcp"]) @@ -206,11 +217,16 @@ impl Scheduler { }; info!("Connecting Tcp to: {}", stream.peer_addr().unwrap()); ( - Protocols::Tcp(TcpProtocol::new(stream, self.metrics.clone())), + Protocols::Tcp(TcpProtocol::new( + stream, + #[cfg(feature = "metrics")] + self.metrics.clone(), + )), false, ) }, ProtocolAddr::Udp(addr) => { + #[cfg(feature = "metrics")] self.metrics .connect_requests_total .with_label_values(&["udp"]) @@ -231,6 +247,7 @@ impl Scheduler { let protocol = UdpProtocol::new( socket.clone(), addr, + #[cfg(feature = "metrics")] self.metrics.clone(), udp_data_receiver, ); @@ -372,7 +389,11 @@ impl Scheduler { }, }; info!("Accepting Tcp from: {}", peer_addr); - let protocol = TcpProtocol::new(stream, self.metrics.clone()); + let protocol = TcpProtocol::new( + stream, + #[cfg(feature = "metrics")] + self.metrics.clone(), + ); self.init_protocol(Protocols::Tcp(protocol), None, true) .await; } @@ -416,6 +437,7 @@ impl Scheduler { let protocol = UdpProtocol::new( socket.clone(), remote_addr, + #[cfg(feature = "metrics")] self.metrics.clone(), udp_data_receiver, ); @@ -474,6 +496,7 @@ impl Scheduler { // the UDP listening is done in another place. let cid = self.channel_ids.fetch_add(1, Ordering::Relaxed); let participants = self.participants.clone(); + #[cfg(feature = "metrics")] let metrics = self.metrics.clone(); let pool = self.pool.clone(); let local_pid = self.local_pid; @@ -486,6 +509,7 @@ impl Scheduler { cid, local_pid, local_secret, + #[cfg(feature = "metrics")] metrics.clone(), send_handshake, ); @@ -506,7 +530,12 @@ impl Scheduler { mut s2b_create_channel_s, s2b_shutdown_bparticipant_s, api_participant_closed, - ) = BParticipant::new(pid, sid, metrics.clone()); + ) = BParticipant::new( + pid, + sid, + #[cfg(feature = "metrics")] + metrics.clone(), + ); let participant = Participant::new( local_pid, @@ -517,6 +546,7 @@ impl Scheduler { api_participant_closed, ); + #[cfg(feature = "metrics")] metrics.participants_connected_total.inc(); participants.insert(pid, ParticipantInfo { secret, diff --git a/network/src/types.rs b/network/src/types.rs index 6b358cad54..3ede7fd302 100644 --- a/network/src/types.rs +++ b/network/src/types.rs @@ -90,8 +90,10 @@ pub(crate) enum Frame { } impl Frame { + #[cfg(feature = "metrics")] pub const FRAMES_LEN: u8 = 8; + #[cfg(feature = "metrics")] pub const fn int_to_string(i: u8) -> &'static str { match i { 0 => "Handshake", @@ -106,6 +108,7 @@ impl Frame { } } + #[cfg(feature = "metrics")] pub fn get_int(&self) -> u8 { match self { Frame::Handshake { .. } => 0, @@ -119,6 +122,7 @@ impl Frame { } } + #[cfg(feature = "metrics")] pub fn get_string(&self) -> &str { Self::int_to_string(self.get_int()) } } @@ -130,7 +134,7 @@ impl Pid { /// use veloren_network::{Network, Pid}; /// /// let pid = Pid::new(); - /// let _ = Network::new(pid, None); + /// let _ = Network::new(pid); /// ``` pub fn new() -> Self { Self { diff --git a/network/tests/helper.rs b/network/tests/helper.rs index f20d29b3cf..53ce8e0c47 100644 --- a/network/tests/helper.rs +++ b/network/tests/helper.rs @@ -49,9 +49,9 @@ pub fn setup(tracing: bool, mut sleep: u64) -> (u64, u64) { pub async fn network_participant_stream( addr: ProtocolAddr, ) -> (Network, Participant, Stream, Network, Participant, Stream) { - let (n_a, f_a) = Network::new(Pid::fake(1), None); + let (n_a, f_a) = Network::new(Pid::fake(1)); std::thread::spawn(f_a); - let (n_b, f_b) = Network::new(Pid::fake(2), None); + let (n_b, f_b) = Network::new(Pid::fake(2)); std::thread::spawn(f_b); n_a.listen(addr.clone()).await.unwrap(); diff --git a/network/tests/integration.rs b/network/tests/integration.rs index be83cb853b..7f37d5e78e 100644 --- a/network/tests/integration.rs +++ b/network/tests/integration.rs @@ -62,8 +62,8 @@ fn stream_simple_udp_3msg() { #[ignore] fn tcp_and_udp_2_connections() -> std::result::Result<(), Box> { let (_, _) = helper::setup(false, 0); - let (network, f) = Network::new(Pid::new(), None); - let (remote, fr) = Network::new(Pid::new(), None); + let (network, f) = Network::new(Pid::new()); + let (remote, fr) = Network::new(Pid::new()); std::thread::spawn(f); std::thread::spawn(fr); block_on(async { @@ -87,7 +87,7 @@ fn tcp_and_udp_2_connections() -> std::result::Result<(), Box std::result::Result<(), Box> { let (_, _) = helper::setup(false, 0); - let (network, f) = Network::new(Pid::new(), None); + let (network, f) = Network::new(Pid::new()); std::thread::spawn(f); let udp1 = udp(); let tcp1 = tcp(); @@ -95,7 +95,7 @@ fn failed_listen_on_used_ports() -> std::result::Result<(), Box std::result::Result<(), Box> let (_, _) = helper::setup(false, 0); // Create a Network, listen on Port `1200` and wait for a Stream to be opened, // then answer `Hello World` - let (network, f) = Network::new(Pid::new(), None); - let (remote, fr) = Network::new(Pid::new(), None); + let (network, f) = Network::new(Pid::new()); + let (remote, fr) = Network::new(Pid::new()); std::thread::spawn(f); std::thread::spawn(fr); block_on(async { @@ -148,8 +148,8 @@ fn api_stream_recv_main() -> std::result::Result<(), Box> let (_, _) = helper::setup(false, 0); // Create a Network, listen on Port `1220` and wait for a Stream to be opened, // then listen on it - let (network, f) = Network::new(Pid::new(), None); - let (remote, fr) = Network::new(Pid::new(), None); + let (network, f) = Network::new(Pid::new()); + let (remote, fr) = Network::new(Pid::new()); std::thread::spawn(f); std::thread::spawn(fr); block_on(async { diff --git a/server/Cargo.toml b/server/Cargo.toml index 2a5215866a..8d52f0b276 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -11,7 +11,7 @@ default = ["worldgen"] [dependencies] common = { package = "veloren-common", path = "../common" } world = { package = "veloren-world", path = "../world" } -network = { package = "veloren_network", path = "../network", default-features = false } +network = { package = "veloren_network", path = "../network", features = ["metrics"], default-features = false } specs-idvs = { git = "https://gitlab.com/veloren/specs-idvs.git", branch = "specs-git" } diff --git a/server/src/lib.rs b/server/src/lib.rs index 88166f25e9..4ed6b6272f 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -53,7 +53,7 @@ use std::{ }; #[cfg(not(feature = "worldgen"))] use test_world::{World, WORLD_SIZE}; -use tracing::{debug, error, info}; +use tracing::{debug, error, info, warn}; use uvth::{ThreadPool, ThreadPoolBuilder}; use vek::*; #[cfg(feature = "worldgen")] @@ -240,7 +240,7 @@ impl Server { let thread_pool = ThreadPoolBuilder::new() .name("veloren-worker".to_string()) .build(); - let (network, f) = Network::new(Pid::new(), None); + let (network, f) = Network::new(Pid::new()); thread_pool.execute(f); block_on(network.listen(ProtocolAddr::Tcp(settings.gameserver_address)))?; @@ -599,7 +599,16 @@ impl Server { loop { let participant = self.network.connected().await?; debug!("New Participant connected to the server"); - let singleton_stream = participant.opened().await?; + let singleton_stream = match participant.opened().await { + Ok(s) => s, + Err(e) => { + warn!( + ?e, + "Failed to open a Stream from remote client. Dropping it" + ); + continue; + }, + }; let mut client = Client { client_state: ClientState::Connected, From 9a54031e158e4ff4979d7bddacd51e67713e592b Mon Sep 17 00:00:00 2001 From: lausek Date: Wed, 15 Jul 2020 20:56:16 +0000 Subject: [PATCH 35/85] de_DE: minor updates on the German translation --- assets/voxygen/i18n/de_DE.ron | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/assets/voxygen/i18n/de_DE.ron b/assets/voxygen/i18n/de_DE.ron index b669edc521..2dccab239c 100644 --- a/assets/voxygen/i18n/de_DE.ron +++ b/assets/voxygen/i18n/de_DE.ron @@ -74,7 +74,7 @@ VoxygenLocalization( "common.connection_lost": r#"Verbindung unterbrochen."#, - "common.species.orc": "Orc", + "common.species.orc": "Ork", "common.species.human": "Mensch", "common.species.dwarf": "Zwerg", "common.species.elf": "Elf", @@ -102,16 +102,16 @@ Bevor es losgeht noch einige Infos: Dies ist eine frühe Alpha. Ihr werdet auf Bugs, unfertiges Gameplay und Mechaniken, sowie fehlende Features stoßen. -Für konstruktives Feedback und Bug-Reports könnt ihr uns via Reddit, Gitlab oder unseren Discord Server kontaktieren. +Für konstruktives Feedback und Bug-Reports könnt Ihr uns via Reddit, Gitlab oder unseren Discord Server kontaktieren. -Veloren hat die GPL 3 Open-Source Lizenz. Das heißt ihr könnt es kostenlos spielen, +Veloren hat die GPL 3 Open-Source Lizenz. Das heißt Ihr könnt es kostenlos spielen, aber auch modifizieren (solange die Mods auch die selbe Lizenz tragen) und das Spiel an andere weiterschicken. Veloren ist ein Non-Profit Community Projekt und jeder Mitarbeiter entwickelt es als Hobby in seiner Freizeit. -Wenn euch die Idee gefällt, dann schließt euch doch einfach unserem Dev- oder Art-Team an! +Wenn Euch die Idee gefällt, dann schließt Euch doch einfach unserem Dev- oder Art-Team an! -Danke, dass ihr euch die Zeit genommen habt diese Zeilen zu lesen und wir hoffen, dass euch Veloren gefällt! +Danke, dass Ihr Euch die Zeit genommen habt diese Zeilen zu lesen und wir hoffen, dass Euch Veloren gefällt! ~ Die Entwickler"#, @@ -120,7 +120,7 @@ Danke, dass ihr euch die Zeit genommen habt diese Zeilen zu lesen und wir hoffen Zum Spielen wird ein Account benötigt. -Diesen könnt ihr euch hier erstellen: +Diesen könnt Ihr Euch hier erstellen: https://account.veloren.net. @@ -161,7 +161,7 @@ eurer erstellen Charaktere gespeichert."#, "hud.chat.loot_msg": "Ihr erhaltet [{item}]", "hud.chat.loot_fail": "Euer Inventar ist voll!", "hud.chat.goodbye": "Verbindung getrennt.", - "hud.chat.connection_lost": "Verbindung unterbrochen. Trenne Verbindung in {time} sekunden.", + "hud.chat.connection_lost": "Verbindung unterbrochen. Trenne Verbindung in {time} Sekunden.", // SCT outputs "hud.sct.experience": "{amount} Erf", @@ -174,7 +174,7 @@ eurer erstellen Charaktere gespeichert."#, "hud.welcome": r#"Willkommen zur Veloren Alpha. -Einige Tipps bevor ihr startet: +Einige Tipps bevor Ihr beginnt: Drückt F1, um die Tastenbelegungen zu sehen. @@ -186,9 +186,9 @@ Um Chat-Kommandos zu sehen gebt /help in den Chat ein. Sammelt diese mit Rechts-Klick auf. -Um diese zu nutzen öffnet euer Inventar mit 'B'. +Um diese zu nutzen öffnet Euer Inventar mit 'B'. -Doppelklickt den Gegenstand in eurer Tasche, um diesen zu nutzen. +Doppelklickt den Gegenstand in Eurer Tasche, um diesen zu nutzen. Um Items wegzuwerfen klickt sie einmal im Inventar an @@ -197,10 +197,10 @@ und klickt dann außerhalb der Tasche. Die Nächte in Veloren können sehr dunkel werden. -Drückt 'G' um eure Laterne einzuschalten. +Drückt 'G' um Eure Laterne einzuschalten. -Ihr wollt endlich spielen und dafür euren Cursor befreien, +Ihr wollt endlich spielen und dafür Euren Cursor befreien, um dieses Fenster zu schließen? Drückt 'TAB'! @@ -216,7 +216,7 @@ Versammelt einige Kämpfer, sucht etwas Nahrung und besiegt ihre abscheulichen Anführer und Akolyten. -Vielleicht könnt ihr sogar einen ihrer +Vielleicht könnt Ihr sogar einen ihrer magischen Gegenstände ergattern?"#, // Inventory @@ -432,4 +432,4 @@ Willenskraft "Hilfe, ich werde angegriffen!", ], } -) \ No newline at end of file +) From 4facf1e3f395ab83a75227d324d263293c6be619 Mon Sep 17 00:00:00 2001 From: Monty Marz Date: Thu, 16 Jul 2020 02:46:10 +0000 Subject: [PATCH 36/85] Fix item images, cleanup debug items folder --- assets/common/items/debug/empty_vial.ron | 7 ------- assets/common/items/debug/leather_scraps.ron | 7 ------- assets/common/items/debug/shiny_gem.ron | 7 ------- assets/common/items/debug/stones.ron | 7 ------- assets/common/items/debug/twigs.ron | 7 ------- assets/voxygen/element/icons/item_apple.png | 4 ++-- assets/voxygen/element/icons/item_cheese.png | 4 ++-- assets/voxygen/element/icons/item_coconut.png | 4 ++-- 8 files changed, 6 insertions(+), 41 deletions(-) delete mode 100644 assets/common/items/debug/empty_vial.ron delete mode 100644 assets/common/items/debug/leather_scraps.ron delete mode 100644 assets/common/items/debug/shiny_gem.ron delete mode 100644 assets/common/items/debug/stones.ron delete mode 100644 assets/common/items/debug/twigs.ron diff --git a/assets/common/items/debug/empty_vial.ron b/assets/common/items/debug/empty_vial.ron deleted file mode 100644 index 422a2870dd..0000000000 --- a/assets/common/items/debug/empty_vial.ron +++ /dev/null @@ -1,7 +0,0 @@ -Item( - name: "Empty Vial", - description: "Can be filled with fluids.", - kind: Ingredient( - kind: EmptyVial, - ) -) diff --git a/assets/common/items/debug/leather_scraps.ron b/assets/common/items/debug/leather_scraps.ron deleted file mode 100644 index cc546f8639..0000000000 --- a/assets/common/items/debug/leather_scraps.ron +++ /dev/null @@ -1,7 +0,0 @@ -Item( - name: "Leather Scraps", - description: "Used to craft various items.", - kind: Ingredient( - kind: LeatherScraps, - ) -) diff --git a/assets/common/items/debug/shiny_gem.ron b/assets/common/items/debug/shiny_gem.ron deleted file mode 100644 index ec396047fa..0000000000 --- a/assets/common/items/debug/shiny_gem.ron +++ /dev/null @@ -1,7 +0,0 @@ -Item( - name: "Shiny Gem", - description: "It's so shiny!", - kind: Ingredient( - kind: ShinyGem, - ) -) diff --git a/assets/common/items/debug/stones.ron b/assets/common/items/debug/stones.ron deleted file mode 100644 index 20d5842255..0000000000 --- a/assets/common/items/debug/stones.ron +++ /dev/null @@ -1,7 +0,0 @@ -Item( - name: "Stones", - description: "Pebbles from the ground.", - kind: Ingredient( - kind: Stones, - ) -) diff --git a/assets/common/items/debug/twigs.ron b/assets/common/items/debug/twigs.ron deleted file mode 100644 index 3eaba0f24a..0000000000 --- a/assets/common/items/debug/twigs.ron +++ /dev/null @@ -1,7 +0,0 @@ -Item( - name: "Twigs", - description: "Dry.", - kind: Ingredient( - kind: Twigs, - ) -) diff --git a/assets/voxygen/element/icons/item_apple.png b/assets/voxygen/element/icons/item_apple.png index 406c97f13a..860aae8b59 100644 --- a/assets/voxygen/element/icons/item_apple.png +++ b/assets/voxygen/element/icons/item_apple.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:01852fbd88c3d390ba33c8aeb64da7eec54eb6ccc798ddc56525ed55ba41f822 -size 338 +oid sha256:5461b53f587f61256bc8ba5472ae397c9a49604bba615a5e70e3d7aef172f450 +size 421 diff --git a/assets/voxygen/element/icons/item_cheese.png b/assets/voxygen/element/icons/item_cheese.png index 3975d73506..6bd7975c5d 100644 --- a/assets/voxygen/element/icons/item_cheese.png +++ b/assets/voxygen/element/icons/item_cheese.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:02643618be3bd63bcf69c41eedbecfa20d28fa510683ac6bad6c26cfec66f463 -size 244 +oid sha256:01c206ada0f3f12a496108699ba8587bc0d8f1549bf448cf74599c27c1b701a1 +size 339 diff --git a/assets/voxygen/element/icons/item_coconut.png b/assets/voxygen/element/icons/item_coconut.png index 69f5f9046e..29828b0cb2 100644 --- a/assets/voxygen/element/icons/item_coconut.png +++ b/assets/voxygen/element/icons/item_coconut.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0307232e80cbbc31cf5ec9d03483cbd0851334caffe09d3a4d7bcb71a77e5097 -size 344 +oid sha256:ecfe0e0c59fe3f4a0dfd94038e6332434fc096a9b9c251453175c6778f1d0e7b +size 354 From d6411f478de5bb8b6a7384ec208a2638b9e5d9bc Mon Sep 17 00:00:00 2001 From: Marcel Date: Thu, 16 Jul 2020 11:34:37 +0000 Subject: [PATCH 37/85] Ismail.assaidi/fr --- assets/voxygen/i18n/fr_FR.ron | 302 ++++++++++++++++++++-------------- 1 file changed, 181 insertions(+), 121 deletions(-) diff --git a/assets/voxygen/i18n/fr_FR.ron b/assets/voxygen/i18n/fr_FR.ron index 42d996bb0d..9facaf3492 100644 --- a/assets/voxygen/i18n/fr_FR.ron +++ b/assets/voxygen/i18n/fr_FR.ron @@ -49,8 +49,12 @@ VoxygenLocalization( "common.yes": "Oui", "common.no": "Non", "common.okay": "Compris", + "common.accept": "Accepter", + "common.disclaimer": "Avertissement", "common.cancel": "Annuler", "common.none": "Aucun", + "common.error": "Erreur", + "common.fatal_error": "Erreur Fatale", "common.species.orc": "Orc", "common.species.human": "Humain", @@ -89,34 +93,58 @@ Merci d'avoir pris le temps de lire cette notice, nous esperons que vous appreci ~ L'équipe de Veloren"#, - + //Informations de connection "main.login_process": r#"Information sur la procédure de connexion: -L'identifiant de connexion sera votre pseudo en jeu. +Vous devez à présent posséder un compte +afin de jouer sur les serveurs avec authentification. -Le nom et l'apparence que vous choisirez seront -sauvegardés sur votre ordinateur. +Vous pouvez créer un compte à l'adresse -Les niveaux/objets ne sont pas sauvegardés."#, +https://account.veloren.net."#, + "main.login.server_not_found": "Serveur introuvable", + "main.login.authentication_error": "Erreur d'authentification sur le serveur", + "main.login.server_full": "Serveur plein", + "main.login.untrusted_auth_server": "Le serveur d'authentification n'est pas de confiance", + "main.login.outdated_client_or_server": "ServeurPasContent: Les versions sont probablement incompatibles, verifiez les mises à jour.", + "main.login.timeout": "DélaiEcoulé: Le serveur n'a pas repondu à temps. (Surchage ou Problèmes réseau).", + "main.login.server_shut_down": "Extinction du Serveur", + "main.login.already_logged_in": "Vous êtes déjà connecté à ce serveur.", + "main.login.network_error": "Problème Réseau", + "main.login.failed_sending_request": "Demande d'authentification serveur échouée", + "main.login.invalid_character": "Le personnage sélectionné n'est pas valide", + "main.login.client_crashed": "Le client a planté", + "main.login.not_on_whitelist": "Vous devez être ajouté à la liste blanche par un Admin pour pouvoir entrer", + /// End Main screen section - // HUD texts - "hud.you_died": "Vous êtes mort", + ///Début section Hud "hud.do_not_show_on_startup": "Ne pas afficher au démarage", - "hud.press_key_to_show_keybindings_fmt": "Appuyer sur {key} pour afficher les contrôles", - "hud.settings.toggle_shortcuts": "Activer les raccourcis", "hud.show_tips": "Voir les astuces", "hud.quests": "Quêtes", - "hud.spell": "Sorts", - "hud.press_key_to_toggle_debug_info_fmt": "Appuyer sur {key} pour activer les informations de debogage", + "hud.you_died": "Vous êtes mort", + "hud.waypoint_saved": "Point de Repère Sauvegardé", + + + "hud.press_key_to_show_keybindings_fmt": "Appuyer sur {key} pour afficher les contrôles", "hud.press_key_to_show_debug_info_fmt": "Appuyer sur {key} pour afficher les informations de debogage", + "hud.press_key_to_toggle_debug_info_fmt": "Appuyer sur {key} pour activer les informations de debogage", "hud.press_key_to_toggle_keybindings_fmt": "Appuyer sur {key} pour afficher les contrôles", + // Sorties Tchat + "hud.chat.online_msg": "[{name}] est maintenant en ligne.", + "hud.chat.offline_msg": "{name} s'est déconnecté.", + "hud.chat.loot_msg": "Vous avez ramassé [{item}]", + "hud.chat.loot_fail": "Votre inventaire est plein!", + "hud.chat.goodbye": "Au revoir!", + "hud.chat.connection_lost": "Connection perdue. Expulsion dans {time} secondes.", - /// Respawn message - "hud.press_key_to_respawn": r#"Appuyer sur {key} pour revivre à votre point de repère. + // Sorties SCT + "hud.sct.experience": "{amount} Exp", + "hud.sct.block": "BLOQUÉ", -Appuyez sur Entrée, taper /waypoint pour configurer le point de repère à l'endroit actuel"#, + // Respawn message + "hud.press_key_to_respawn": r#"Appuyez sur {key} pour réapparaitre au dernier feu de camp visité"#, /// Welcome message @@ -153,54 +181,92 @@ Vous souhaitez libérer votre souris pour fermer cette fenêtre? Tapez sur TAB! Profitez de votre séjour dans le monde de Veloren."#, -"hud.temp_quest_headline": r#"Please, help us Traveller!"#, -"hud.temp_quest_text": r#"Dungeons filled with evil cultists -have emerged all around our peaceful towns! +"hud.temp_quest_headline": r#"S'il vous plaît, aidez nous voyageur!"#, +"hud.temp_quest_text": r#"Des donjons remplis de cultistes malfaisants +sont apparus tout autour de nos paisibles villages! -Gather some company, stack up on food -and defeat their vile leaders and acolytes. +Trouvez des alliés, faites des provisions +et venez à bout de leurs chefs ainsi que de leurs sbires. -Maybe you can even obtain one of their -magically infused items?"#, +Peut être pourrez-vous même obtenir un de leurs +objets magiques ?"#, + + + // Inventaire + "hud.bag.inventory": "Inventaire de {playername}", + "hud.bag.stats_title": "Attributs de {playername}", + "hud.bag.exp": "Exp", + "hud.bag.armor": "Armure", + "hud.bag.stats": "Attributs", + "hud.bag.head": "Tête", + "hud.bag.neck": "Cou", + "hud.bag.tabard": "Tabar", + "hud.bag.shoulders": "Epaules", + "hud.bag.chest": "Torse", + "hud.bag.hands": "Mains", + "hud.bag.lantern": "Lanterne", + "hud.bag.belt": "Ceinture", + "hud.bag.ring": "Bague", + "hud.bag.back": "Dos", + "hud.bag.legs": "Jambes", + "hud.bag.feet": "Pieds", + "hud.bag.mainhand": "Main Dominante", + "hud.bag.offhand": "Main Dominée", + + // Carte et journal de quetes + "hud.map.map_title": "Carte", + "hud.map.qlog_title": "Quêtes", + + + //Paramètres "hud.settings.general": "Général", + "hud.settings.none": "Aucun", + "hud.settings.press_behavior.toggle": "Activer/Desactiver", + "hud.settings.press_behavior.hold": "Maintenir", "hud.settings.help_window": "Fenêtre d'aide", "hud.settings.debug_info": "Information de débogage", "hud.settings.tips_on_startup": "Astuces au démarrage", - "hud.settings.ui_scale": "Echelle de l'interface", "hud.settings.relative_scaling": "Echelle relative", "hud.settings.custom_scaling": "Echelle personalisée", - "hud.settings.crosshair": "Réticule", "hud.settings.transparency": "Transparence", - "hud.settings.hotbar": "Barre active", + "hud.settings.hotbar": "Barre d'action", + "hud.settings.toggle_shortcuts": "Activer les raccourcis", "hud.settings.toggle_bar_experience": "Activer la barre d'experience", - "hud.settings.scrolling_combat_text": "Dégats de combat", "hud.settings.single_damage_number": "Dégat adversaire (par dégat)", "hud.settings.cumulated_damage": "Dégat adversaire (cumulé)", "hud.settings.incoming_damage": "Dégat personnage (par dégat)", "hud.settings.cumulated_incoming_damage": "Dégat personnage (cumulé)", - + "hud.settings.speech_bubble": "Bulle de dialogue", + "hud.settings.speech_bubble_dark_mode": "Bulle de dialogue Mode Sombre", + "hud.settings.speech_bubble_icon": "Icône Bulle de dialogue", "hud.settings.energybar_numbers": "Nombre des barres d'energie", - "hud.settings.none": "Aucuns", "hud.settings.values": "Valeurs", "hud.settings.percentages": "Pourcentages", - "hud.settings.chat": "Tchat", "hud.settings.background_transparency": "Transparence du fond", + "hud.settings.chat_character_name": "Nom des personnages dans le tchat", "hud.settings.pan_sensitivity": "Sensibilité de la souris", "hud.settings.zoom_sensitivity": "Sensibilité du zoom", "hud.settings.invert_scroll_zoom": "Inverser la molette", "hud.settings.invert_mouse_y_axis": "Inverser l'axe Y", + "hud.settings.enable_mouse_smoothing": "Lissage Camera", + "hud.settings.free_look_behavior": "Comportement Vue libre", + "hud.settings.auto_walk_behavior": "Comportement Marche Automatique", + "hud.settings.stop_auto_walk_on_input": "Arrêt marche auto si mouvement", "hud.settings.view_distance": "Distance d'affichage", + "hud.settings.sprites_view_distance": "Distance d'affichage des sprites", + "hud.settings.figures_view_distance": "Distance d'affichage des entités", "hud.settings.maximum_fps": "Limite FPS", "hud.settings.fov": "Champs de vision (degrés)", + "hud.settings.gamma": "Gamma", "hud.settings.antialiasing_mode": "Mode anticrénelage", "hud.settings.cloud_rendering_mode": "Rendu des nuages", "hud.settings.fluid_rendering_mode": "Rendu des fluides", @@ -214,87 +280,9 @@ magically infused items?"#, "hud.settings.sound_effect_volume": "Volume des effets", "hud.settings.audio_device": "Périphérique audio", - "hud.settings.control_names": r#"Libérer le curseur -Activer la fenêtre d'aide -Activer l'interface -Activer la fenêtre de débogage -Prendre une capture d'écran -Activer les noms de personnage -Activer le mode plein écran - - -Avancer -Aller à gauche -Aller à droite -Reculer - -Sauter - -Planer - -Esquiver - -Roulade - -Escalader - -Desescalader - -Marche automatique - -Ranger/Sortir les armes - -Mettre/Enlever le casque - -S'assoir - -Monter - -Intéragir - - -Attaque basique -Attaque secondaire/Bloquer/Viser - - -Barre de compétence 1 -Barre de compétence 2 -Barre de compétence 3 -Barre de compétence 4 -Barre de compétence 5 -Barre de compétence 6 -Barre de compétence 7 -Barre de compétence 8 -Barre de compétence 9 -Barre de compétence 10 - - -Pause -Paramètres -Social -Carte -Livre de sorts -Personnages -Livre de quêtes -Sac à dos - - - -Envoyer un message -Défiler dans le tchat - - -Commandes du tchat: - -/alias [Nom] - Changer de nom -/tp [Nom] - Se téléporter vers un autre joueur -/jump - Sauter à partir de votre position -/goto - Se téléporter à une position -/kill - Se suicider -/pig - Faire aparaitre un cochon PNJ -/wolf - Faire aparaitre un loup PNJ -/help - Afficher les commandes de tchat"#, - + "hud.settings.awaitingkey": "Appuyez sur une touche...", + "hud.settings.unbound": "Aucun", + "hud.settings.reset_keybinds": "Réinitialiser touches par défaut", "hud.social": "Social", "hud.social.friends": "Amis", @@ -302,30 +290,102 @@ Commandes du tchat: "hud.social.online": "Jeu en ligne", "hud.social.not_yet_available": "Pas encore disponible", "hud.social.play_online_fmt": "{nb_player} joueurs en ligne", - "char_selection.change_server": "Changer de serveur", - "char_selection.delete_permanently": "Supprimer", + "hud.crafting": "Fabrication", + "hud.crafting.recipes": "Recettes", + "hud.crafting.ingredients": "Ingrédients:", + "hud.crafting.craft": "Fabriquer", + "hud.crafting.tool_cata": "Nécessite:", + "hud.spell": "Sorts", + + "hud.free_look_indicator": "Vue libre active", + "hud.auto_walk_indicator": "Marche automatique active", + + //Fin Section Hud + + /// Debut de section GameInput + "gameinput.primary": "Attaque Basique", + "gameinput.secondary": "Attaque Secondaire/Bloquer/Viser", + "gameinput.slot1": "Emplacement rapide 1", + "gameinput.slot2": "Emplacement rapide 2", + "gameinput.slot3": "Emplacement rapide 3", + "gameinput.slot4": "Emplacement rapide 4", + "gameinput.slot5": "Emplacement rapide 5", + "gameinput.slot6": "Emplacement rapide 6", + "gameinput.slot7": "Emplacement rapide 7", + "gameinput.slot8": "Emplacement rapide 8", + "gameinput.slot9": "Emplacement rapide 9", + "gameinput.slot10": "Emplacement rapide 10", + "gameinput.swaploadout": "Échanger l'équipement", + "gameinput.togglecursor": "Activer/Desactiver Curseur", + "gameinput.help": "Activer/Desactiver Fenêtre d'aide", + "gameinput.toggleinterface": "Activer/Desactiver Interface", + "gameinput.toggledebug": "Activer/Desactiver IPS et Infos Debogage", + "gameinput.screenshot": "Prendre une capture d'écran", + "gameinput.toggleingameui": "Activer/Desactiver Noms de joueurs", + "gameinput.fullscreen": "Activer/Desactiver Plein Ecran", + "gameinput.moveforward": "Avancer", + "gameinput.moveleft": "Aller à Gauche", + "gameinput.moveright": "Aller à Droite", + "gameinput.moveback": "Reculer", + "gameinput.jump": "Sauter", + "gameinput.glide": "Planeur", + "gameinput.roll": "Rouler", + "gameinput.climb": "Grimper", + "gameinput.climbdown": "Descendre", + "gameinput.wallleap": "Saut Mural", + "gameinput.togglelantern": "Activer/Desactiver Lanterne", + "gameinput.mount": "Monture", + "gameinput.enter": "Entrer", + "gameinput.command": "Commande", + "gameinput.escape": "Fuir", + "gameinput.map": "Carte", + "gameinput.bag": "Sac", + "gameinput.social": "Social", + "gameinput.sit": "S'asseoir", + "gameinput.spellbook": "Sorts", + "gameinput.settings": "Paramètres", + "gameinput.respawn": "Réapparaître", + "gameinput.charge": "Charger", + "gameinput.togglewield": "Ranger Arme", + "gameinput.interact": "Interagir", + "gameinput.freelook": "Vue Libre", + "gameinput.autowalk": "Marche Automatique", + "gameinput.dance": "Dancer", + + /// End GameInput section + + /// Debut Section Menu Start Quitter "esc_menu.quit_game": "Quitter le jeu", "esc_menu.logout": "Se déconnecter", + /// Fin Section Menu Start Quitter + /// Debut de la section Création du personnage + "char_selection.accessories": "Accessoires", "char_selection.beard": "Barbe", - "char_selection.hair_style": "Coupe de cheveux", - "char_selection.hair_color": "Couleur des cheveux", - "char_selection.skin": "Couleur de peau", + "char_selection.create_new_charater": "Créer un nouveau personnage", + "char_selection.creating_character": "Création du personnage...", + "char_selection.character_creation": "Création de personnages", + "char_selection.create_info_name": "Votre personnage doit avoir un prénom !", + "char_selection.deleting_character": "Suppression du personnage...", + "char_selection.enter_world": "Entrer dans le monde", "char_selection.eyebrows": "Sourcils", "char_selection.eye_color": "Couleur des yeux", - "char_selection.accessories": "Accessoires", - "char_selection.level_fmt": "Niveau {level_nb}", - "char_selection.create_new_charater": "Créer un nouveau personnage", + "char_selection.eyeshape": "Forme des yeux", + "char_selection.hair_style": "Coupe de cheveux", + "char_selection.hair_color": "Couleur des cheveux", "char_selection.human_default": "Humain par défault", - "char_selection.uncanny_valley": "Vallée dérangeante", - "char_selection.chest_color": "Couleur du torse", + "char_selection.level_fmt": "Niveau {level_nb}", "char_selection.logout": "Se déconnecter", - "char_selection.enter_world": "Entrer dans le monde", + "char_selection.loading_characters": "Chargement des personnages...", "char_selection.plains_of_uncertainty": "Plaines de l'incertitude", - "char_selection.character_creation": "Création de personnages", + "char_selection.skin": "Couleur de peau", + "char_selection.uncanny_valley": "Vallée dérangeante", + "char_selection.change_server": "Changer de serveur", + "char_selection.delete_permanently": "Supprimer définitivement ce personnage ?", + /// Fin de la section Création du personnage "character_window.character_name": "Personnage", "character_window.character_stats": r#"Endurance From f49b4a990850cd87d52eb5c04dfb426d136a3a4f Mon Sep 17 00:00:00 2001 From: Yusuf Bera Ertan Date: Thu, 16 Jul 2020 13:40:39 +0000 Subject: [PATCH 38/85] nix: regenerate Cargo.nix, update dependencies, some tweaks for better reproducibility, remove rustup from shell.nix (causes issues) and add rustc, cargo instead --- nix/.envrc | 1 + nix/Cargo.nix | 1109 +++++++++++++++++------------------------ nix/crate-hashes.json | 3 +- nix/default.nix | 50 +- nix/nixpkgs.nix | 13 +- nix/shell.nix | 10 +- nix/sources.json | 12 +- nix/sources.nix | 4 +- 8 files changed, 507 insertions(+), 695 deletions(-) create mode 100644 nix/.envrc diff --git a/nix/.envrc b/nix/.envrc new file mode 100644 index 0000000000..1d953f4bd7 --- /dev/null +++ b/nix/.envrc @@ -0,0 +1 @@ +use nix diff --git a/nix/Cargo.nix b/nix/Cargo.nix index bc02b0d6ac..73e490f88a 100644 --- a/nix/Cargo.nix +++ b/nix/Cargo.nix @@ -180,24 +180,7 @@ rec { authors = [ "Remi Rampin " ]; }; - "ahash 0.2.18" = rec { - crateName = "ahash"; - version = "0.2.18"; - edition = "2018"; - sha256 = "1lxmn0igyizs10fasgjr4mki97wa4d7ijygjvk0lc28jiw0vacvg"; - authors = [ "Tom Kaitchuck " ]; - dependencies = [{ - name = "const-random"; - packageId = "const-random"; - optional = true; - }]; - features = { - "compile-time-rng" = [ "const-random" ]; - "default" = [ "compile-time-rng" ]; - }; - resolvedDefaultFeatures = [ "compile-time-rng" "const-random" ]; - }; - "ahash 0.3.8" = rec { + "ahash" = rec { crateName = "ahash"; version = "0.3.8"; edition = "2018"; @@ -312,15 +295,6 @@ rec { }]; }; - "anyhow" = rec { - crateName = "anyhow"; - version = "1.0.31"; - edition = "2018"; - sha256 = "0pqrpvlaicjpaf5zfqgwq1rg39gfvqhs9sz6a1acm5zc13671fw5"; - authors = [ "David Tolnay " ]; - features = { "default" = [ "std" ]; }; - resolvedDefaultFeatures = [ "default" "std" ]; - }; "anymap" = rec { crateName = "anymap"; version = "0.12.1"; @@ -378,7 +352,7 @@ rec { } { name = "syn"; - packageId = "syn 1.0.31"; + packageId = "syn 1.0.33"; features = [ "full" ]; } ]; @@ -407,7 +381,6 @@ rec { "default" = [ "std" ]; "serde-1" = [ "serde" ]; }; - resolvedDefaultFeatures = [ "default" "std" ]; }; "arrayvec 0.5.1" = rec { crateName = "arrayvec"; @@ -416,6 +389,7 @@ rec { sha256 = "1f5mca8kiiwhvhxd1mbnq68j6v6rk139sch567zwwzl6hs37vxyg"; authors = [ "bluss" ]; features = { "default" = [ "std" ]; }; + resolvedDefaultFeatures = [ "default" "std" ]; }; "ascii" = rec { crateName = "ascii"; @@ -1334,24 +1308,6 @@ rec { } ]; - }; - "chashmap" = rec { - crateName = "chashmap"; - version = "2.2.2"; - edition = "2015"; - sha256 = "0igsvpc2ajd6w68w4dwn0fln6yww8gq4pq9x02wj36g3q71a6hgz"; - authors = [ "ticki " ]; - dependencies = [ - { - name = "owning_ref"; - packageId = "owning_ref"; - } - { - name = "parking_lot"; - packageId = "parking_lot 0.4.8"; - } - ]; - }; "chrono" = rec { crateName = "chrono"; @@ -1637,31 +1593,6 @@ rec { ]; }; - "colored" = rec { - crateName = "colored"; - version = "1.9.3"; - edition = "2015"; - sha256 = "0nbc1czs512h1k696y7glv1kjrb2b914zpxraic6q5fgv80wizzl"; - authors = [ "Thomas Wickham " ]; - dependencies = [ - { - name = "atty"; - packageId = "atty"; - } - { - name = "lazy_static"; - packageId = "lazy_static"; - } - { - name = "winapi"; - packageId = "winapi 0.3.8"; - usesDefaultFeatures = false; - target = { target, features }: target."windows"; - features = [ "consoleapi" "processenv" "winbase" ]; - } - ]; - features = { }; - }; "conrod_core" = rec { crateName = "conrod_core"; version = "0.63.0"; @@ -1800,15 +1731,11 @@ rec { }; "const-tweaker" = rec { crateName = "const-tweaker"; - version = "0.2.6"; + version = "0.3.1"; edition = "2018"; - sha256 = "0l7jz1rdxixs2d7dpbywsqzj7z0b1a8qdd5da7a9dn6c5wfkxgkz"; + sha256 = "0xp2wxfbksbnra7pbgcm3nk34g28782qsgdhgc66hvlvqgnf4yr9"; authors = [ "Thomas Versteeg " ]; dependencies = [ - { - name = "anyhow"; - packageId = "anyhow"; - } { name = "async-std"; packageId = "async-std"; @@ -1817,6 +1744,10 @@ rec { name = "const-tweaker-attribute"; packageId = "const-tweaker-attribute"; } + { + name = "ctor"; + packageId = "ctor"; + } { name = "dashmap"; packageId = "dashmap"; @@ -1843,9 +1774,9 @@ rec { }; "const-tweaker-attribute" = rec { crateName = "const-tweaker-attribute"; - version = "0.4.1"; + version = "0.5.0"; edition = "2018"; - sha256 = "1fpi3h7qmhr8qpdfdi93l0s630l4zfadwx62r14vjfxxzs7x4hqs"; + sha256 = "1n60l09hb0h2hiz2csvvbn3az0qzmaqfyfhbih60q47ny0w9ww1d"; procMacro = true; authors = [ "Thomas Versteeg " ]; dependencies = [ @@ -1863,7 +1794,7 @@ rec { } { name = "syn"; - packageId = "syn 1.0.31"; + packageId = "syn 1.0.33"; } ]; @@ -2083,9 +2014,9 @@ rec { }; "cpal" = rec { crateName = "cpal"; - version = "0.10.0"; + version = "0.11.0"; edition = "2015"; - sha256 = "084142l0k28x2f3v8raw1rg3kbmqf15a9qz1b5dhp1dy9410gv9x"; + sha256 = "0nfx8d227pcfgb81a3y7418xzbp066s08g5xjlmgc0zld5fxambb"; authors = [ "The CPAL contributors" "Pierre Krieger " @@ -2112,10 +2043,6 @@ rec { ((target."os" == "macos") || (target."os" == "ios")); features = [ "audio_unit" "core_audio" ]; } - { - name = "failure"; - packageId = "failure"; - } { name = "lazy_static"; packageId = "lazy_static"; @@ -2137,6 +2064,10 @@ rec { usesDefaultFeatures = false; target = { target, features }: (target."os" == "emscripten"); } + { + name = "thiserror"; + packageId = "thiserror"; + } { name = "winapi"; packageId = "winapi 0.3.8"; @@ -2154,6 +2085,7 @@ rec { "objbase" "std" "synchapi" + "winbase" "winuser" ]; } @@ -2209,7 +2141,7 @@ rec { } { name = "itertools"; - packageId = "itertools 0.9.0"; + packageId = "itertools"; } { name = "lazy_static"; @@ -2280,7 +2212,7 @@ rec { } { name = "itertools"; - packageId = "itertools 0.9.0"; + packageId = "itertools"; } ]; @@ -2625,6 +2557,27 @@ rec { features = { "libc" = [ "memchr/libc" ]; }; resolvedDefaultFeatures = [ "default" ]; }; + "ctor" = rec { + crateName = "ctor"; + version = "0.1.15"; + edition = "2018"; + sha256 = "09x2my9x33srjdip8yf4lm5gq7xqis2694abvpa64r60pajqm19r"; + procMacro = true; + authors = [ "Matt Mastracci " ]; + dependencies = [ + { + name = "quote"; + packageId = "quote 1.0.7"; + } + { + name = "syn"; + packageId = "syn 1.0.33"; + usesDefaultFeatures = false; + features = [ "full" "parsing" "printing" "proc-macro" ]; + } + ]; + + }; "daggy" = rec { crateName = "daggy"; version = "0.5.0"; @@ -2691,7 +2644,7 @@ rec { } { name = "syn"; - packageId = "syn 1.0.31"; + packageId = "syn 1.0.33"; features = [ "full" "extra-traits" ]; } ]; @@ -2716,7 +2669,7 @@ rec { } { name = "syn"; - packageId = "syn 1.0.31"; + packageId = "syn 1.0.33"; } ]; @@ -2730,7 +2683,7 @@ rec { dependencies = [ { name = "ahash"; - packageId = "ahash 0.3.8"; + packageId = "ahash"; } { name = "cfg-if"; @@ -2774,30 +2727,6 @@ rec { ]; features = { "gzip" = [ "gzip-header" ]; }; }; - "derivative" = rec { - crateName = "derivative"; - version = "1.0.4"; - edition = "2015"; - sha256 = "0dbiw51q4b33gwhkicai06xym0hb6fkid9xn24h3x2k68qsqhv9w"; - procMacro = true; - authors = [ "mcarton " ]; - dependencies = [ - { - name = "proc-macro2"; - packageId = "proc-macro2 0.4.30"; - } - { - name = "quote"; - packageId = "quote 0.6.13"; - } - { - name = "syn"; - packageId = "syn 0.15.44"; - features = [ "visit" "extra-traits" ]; - } - ]; - features = { "test-nightly" = [ "trybuild" ]; }; - }; "deunicode" = rec { crateName = "deunicode"; version = "1.1.1"; @@ -2883,7 +2812,7 @@ rec { } { name = "syn"; - packageId = "syn 1.0.31"; + packageId = "syn 1.0.33"; features = [ "full" "fold" ]; } ]; @@ -2909,30 +2838,30 @@ rec { features = { }; resolvedDefaultFeatures = [ "default" ]; }; - "directories" = rec { - crateName = "directories"; - version = "2.0.2"; - edition = "2015"; - sha256 = "071pjx760m0dccdxlhwsz9m0zl180hxwlag62bydfl54fa0pf6jm"; - authors = [ "Simon Ochsenreither " ]; + "directories-next" = rec { + crateName = "directories-next"; + version = "1.0.1"; + edition = "2018"; + sha256 = "0j2sb1rmhn2lkfacq1qasa1rl7c8cv2iff1qqwnpxv2vji7a1si1"; + authors = [ "The @xdg-rs members" ]; dependencies = [ { name = "cfg-if"; packageId = "cfg-if"; } { - name = "dirs-sys"; - packageId = "dirs-sys"; + name = "dirs-sys-next"; + packageId = "dirs-sys-next"; } ]; }; - "dirs-sys" = rec { - crateName = "dirs-sys"; - version = "0.3.5"; + "dirs-sys-next" = rec { + crateName = "dirs-sys-next"; + version = "0.1.0"; edition = "2015"; - sha256 = "0ym5843xack45b1yjahrh3q2f72shnwf1dd2jncf9qsxf3sxg4wf"; - authors = [ "Simon Ochsenreither " ]; + sha256 = "16iw13jarlagihbiq3n5sd8wfl7vpra089i3h8a2cfcmm2wgfq4w"; + authors = [ "The @xdg-rs members" ]; dependencies = [ { name = "libc"; @@ -3055,43 +2984,6 @@ rec { features = { "default" = [ "use_std" ]; }; resolvedDefaultFeatures = [ "default" "use_std" ]; }; - "env_logger" = rec { - crateName = "env_logger"; - version = "0.6.2"; - edition = "2015"; - sha256 = "1lx2s5nk96xx4i3m4zc4ghqgi8kb07dsnyiv8jk2clhax42dxz5a"; - authors = [ "The Rust Project Developers" ]; - dependencies = [ - { - name = "atty"; - packageId = "atty"; - optional = true; - } - { - name = "humantime"; - packageId = "humantime"; - optional = true; - } - { - name = "log"; - packageId = "log"; - features = [ "std" ]; - } - { - name = "regex"; - packageId = "regex"; - optional = true; - } - { - name = "termcolor"; - packageId = "termcolor"; - optional = true; - } - ]; - features = { "default" = [ "termcolor" "atty" "humantime" "regex" ]; }; - resolvedDefaultFeatures = - [ "atty" "default" "humantime" "regex" "termcolor" ]; - }; "error-chain" = rec { crateName = "error-chain"; version = "0.12.2"; @@ -3233,7 +3125,7 @@ rec { } { name = "syn"; - packageId = "syn 1.0.31"; + packageId = "syn 1.0.33"; } { name = "synstructure"; @@ -3242,35 +3134,6 @@ rec { ]; features = { }; }; - "fern" = rec { - crateName = "fern"; - version = "0.5.9"; - edition = "2018"; - sha256 = "1anslk0hx9an4ypcaxqff080hgbcxm7ji7d4qf4f6qx1mkav16p6"; - authors = [ "David Ross " ]; - dependencies = [ - { - name = "chrono"; - packageId = "chrono"; - } - { - name = "colored"; - packageId = "colored"; - optional = true; - } - { - name = "log"; - packageId = "log"; - features = [ "std" ]; - } - ]; - features = { - "reopen-03" = [ "reopen" "libc" ]; - "syslog-3" = [ "syslog3" ]; - "syslog-4" = [ "syslog4" ]; - }; - resolvedDefaultFeatures = [ "colored" ]; - }; "filetime" = rec { crateName = "filetime"; version = "0.2.10"; @@ -3628,7 +3491,7 @@ rec { "std" = [ "futures-core/std" "futures-task/std" "futures-util/std" ]; "thread-pool" = [ "std" "num_cpus" ]; }; - resolvedDefaultFeatures = [ "num_cpus" "std" "thread-pool" ]; + resolvedDefaultFeatures = [ "default" "num_cpus" "std" "thread-pool" ]; }; "futures-io" = rec { crateName = "futures-io"; @@ -3664,7 +3527,7 @@ rec { } { name = "syn"; - packageId = "syn 1.0.31"; + packageId = "syn 1.0.33"; features = [ "full" ]; } ]; @@ -4448,9 +4311,9 @@ rec { }; "git2" = rec { crateName = "git2"; - version = "0.10.2"; + version = "0.13.6"; edition = "2018"; - sha256 = "09ks0gsg14s6h65bzrrsl7vn0n7jmj7ffk2syim621m9m0gga6kw"; + sha256 = "07nkyk407bbcyrwqmvn855bnab50nk5yjws2pz253rw0544b5r0i"; authors = [ "Josh Triplett " "Alex Crichton " @@ -5173,14 +5036,14 @@ rec { }; "hashbrown" = rec { crateName = "hashbrown"; - version = "0.6.3"; + version = "0.7.2"; edition = "2018"; - sha256 = "1bbf9k46v57zi41m6hjwn83rjldyipv5zwxmdsa7a9c1rb876q4f"; + sha256 = "1ks110dbp81ddn3v826vnrlk5psh3vgvwf4rmb9s0gfdpyb2wa4n"; authors = [ "Amanieu d'Antras " ]; dependencies = [ { name = "ahash"; - packageId = "ahash 0.2.18"; + packageId = "ahash"; optional = true; usesDefaultFeatures = false; } @@ -5198,7 +5061,7 @@ rec { ]; buildDependencies = [{ name = "autocfg"; - packageId = "autocfg 0.1.7"; + packageId = "autocfg 1.0.0"; }]; devDependencies = [{ name = "rayon"; @@ -5206,7 +5069,7 @@ rec { }]; features = { "ahash-compile-time-rng" = [ "ahash/compile-time-rng" ]; - "default" = [ "ahash" "ahash-compile-time-rng" "inline-more" ]; + "default" = [ "ahash" "inline-more" ]; "rustc-dep-of-std" = [ "nightly" "core" @@ -5215,15 +5078,8 @@ rec { "rustc-internal-api" ]; }; - resolvedDefaultFeatures = [ - "ahash" - "ahash-compile-time-rng" - "default" - "inline-more" - "nightly" - "rayon" - "serde" - ]; + resolvedDefaultFeatures = + [ "ahash" "default" "inline-more" "nightly" "rayon" "serde" ]; }; "hermit-abi" = rec { crateName = "hermit-abi"; @@ -5426,18 +5282,6 @@ rec { features = { "default" = [ "std" ]; }; resolvedDefaultFeatures = [ "default" "std" ]; }; - "humantime" = rec { - crateName = "humantime"; - version = "1.3.0"; - edition = "2015"; - sha256 = "0krwgbf35pd46xvkqg14j070vircsndabahahlv3rwhflpy4q06z"; - authors = [ "Paul Colomiets " ]; - dependencies = [{ - name = "quick-error"; - packageId = "quick-error"; - }]; - - }; "hyper" = rec { crateName = "hyper"; version = "0.12.35"; @@ -5794,21 +5638,7 @@ rec { }]; }; - "itertools 0.8.2" = rec { - crateName = "itertools"; - version = "0.8.2"; - edition = "2015"; - sha256 = "1154j48aw913v5jnyhpxialxhdn2sfpl4d7bwididyb1r05jsspm"; - authors = [ "bluss" ]; - dependencies = [{ - name = "either"; - packageId = "either"; - usesDefaultFeatures = false; - }]; - features = { "default" = [ "use_std" ]; }; - resolvedDefaultFeatures = [ "default" "use_std" ]; - }; - "itertools 0.9.0" = rec { + "itertools" = rec { crateName = "itertools"; version = "0.9.0"; edition = "2018"; @@ -5920,10 +5750,10 @@ rec { }; "lewton" = rec { crateName = "lewton"; - version = "0.9.4"; + version = "0.10.1"; edition = "2015"; - sha256 = "1l4bc88cpr8p94dfycykn8gajg20kp611kx159fc8dkh64d2qm4d"; - type = [ "lib" "cdylib" ]; + sha256 = "03bwszwdra225y8i6061m799pzy1kg130mr13vma0jqzjykvwhmy"; + type = [ "lib" "staticlib" ]; authors = [ "est31 " ]; dependencies = [ { @@ -5936,8 +5766,9 @@ rec { optional = true; } { - name = "smallvec"; - packageId = "smallvec 0.6.13"; + name = "tinyvec"; + packageId = "tinyvec"; + features = [ "alloc" ]; } ]; devDependencies = [{ @@ -5965,9 +5796,9 @@ rec { }; "libgit2-sys" = rec { crateName = "libgit2-sys"; - version = "0.9.2"; + version = "0.12.7+1.0.0"; edition = "2018"; - sha256 = "05jayrq6km6fvh65grqr2i9mikvfvwfjrlhc2n1zngh6ys0wfw28"; + sha256 = "1q2k647p267cam1chmyzrz676xnjlhyfbzgca69ppjwvcil7kl5w"; libName = "libgit2_sys"; libPath = "lib.rs"; authors = [ @@ -6047,10 +5878,10 @@ rec { }; "libsqlite3-sys" = rec { crateName = "libsqlite3-sys"; - version = "0.9.3"; - edition = "2015"; - sha256 = "1a2wjlf4xn97v4mbwf20wxb54xd863m0cbddb0j1s251j7yiswfk"; - authors = [ "John Gallagher " ]; + version = "0.18.0"; + edition = "2018"; + sha256 = "1ggpbnis0rci97ln628y2v6pkgfhb6zgc8rsp444mkdfph14lw0y"; + authors = [ "The rusqlite developers" ]; buildDependencies = [ { name = "cc"; @@ -6071,18 +5902,18 @@ rec { ]; features = { "buildtime_bindgen" = [ "bindgen" "pkg-config" "vcpkg" ]; - "bundled" = [ "cc" ]; + "bundled" = [ "cc" "bundled_bindings" ]; + "bundled-windows" = [ "cc" "bundled_bindings" ]; "default" = [ "min_sqlite_version_3_6_8" ]; - "min_sqlite_version_3_6_11" = [ "pkg-config" "vcpkg" ]; "min_sqlite_version_3_6_23" = [ "pkg-config" "vcpkg" ]; "min_sqlite_version_3_6_8" = [ "pkg-config" "vcpkg" ]; "min_sqlite_version_3_7_16" = [ "pkg-config" "vcpkg" ]; - "min_sqlite_version_3_7_3" = [ "pkg-config" "vcpkg" ]; - "min_sqlite_version_3_7_4" = [ "pkg-config" "vcpkg" ]; "min_sqlite_version_3_7_7" = [ "pkg-config" "vcpkg" ]; + "session" = [ "preupdate_hook" ]; }; resolvedDefaultFeatures = [ "bundled" + "bundled_bindings" "cc" "default" "min_sqlite_version_3_6_8" @@ -6399,7 +6230,7 @@ rec { } { name = "syn"; - packageId = "syn 1.0.31"; + packageId = "syn 1.0.33"; features = [ "extra-traits" ]; } ]; @@ -6847,9 +6678,9 @@ rec { }; "notify" = rec { crateName = "notify"; - version = "5.0.0-pre.2"; + version = "5.0.0-pre.3"; edition = "2018"; - sha256 = "1hvkssakq5iac8aizhq2xynr9srg0sdy20n3k1azpgw8a6vc003v"; + sha256 = "03kxlcnpg8xkhp3g04y8basy7kpkzwjx97hfp9hb3d48rw3kdl3p"; authors = [ "Félix Saparelli " "Daniel Faust " @@ -6863,10 +6694,6 @@ rec { name = "bitflags"; packageId = "bitflags"; } - { - name = "chashmap"; - packageId = "chashmap"; - } { name = "crossbeam-channel"; packageId = "crossbeam-channel 0.4.2"; @@ -7077,9 +6904,9 @@ rec { }; "num-integer" = rec { crateName = "num-integer"; - version = "0.1.42"; + version = "0.1.43"; edition = "2015"; - sha256 = "1fpw8yr9xwsf3qrh91rm7mzqaiwlc2dmnalsxv9pr9w1klpacviz"; + sha256 = "0nw79ynfvw8br6yncv27pw65y2vw2z7m3kv9g2hinm1dcrz4ancd"; authors = [ "The Rust Project Developers" ]; dependencies = [{ name = "num-traits"; @@ -7099,9 +6926,9 @@ rec { }; "num-iter" = rec { crateName = "num-iter"; - version = "0.1.40"; + version = "0.1.41"; edition = "2015"; - sha256 = "005wif3bk23b5jdg7l0cprzqzyc4jg0xjyzyykciv2ci08581c6z"; + sha256 = "17sb142lhmpsq17cf9wrffjh8vjk901axxf55565r6cgfiy6nvks"; authors = [ "The Rust Project Developers" ]; dependencies = [ { @@ -7165,9 +6992,9 @@ rec { }; "num-traits" = rec { crateName = "num-traits"; - version = "0.2.11"; + version = "0.2.12"; edition = "2015"; - sha256 = "15khrlm1bra50nd48ijl1vln13m9xg4fxzghf28jp16ic5zf8ay6"; + sha256 = "04fnzwlnn6fcy09jjbi9l7bj5dvg657x5c2sjgwfb3pl0z67n9mc"; authors = [ "The Rust Project Developers" ]; buildDependencies = [{ name = "autocfg"; @@ -7393,18 +7220,6 @@ rec { packageId = "shared_library"; }]; - }; - "owning_ref" = rec { - crateName = "owning_ref"; - version = "0.3.3"; - edition = "2015"; - sha256 = "0dqgf5hwbmvkf2ffbik5xmhvaqvqi6iklhwk9x47n0wycd0lzy6d"; - authors = [ "Marvin Löbel " ]; - dependencies = [{ - name = "stable_deref_trait"; - packageId = "stable_deref_trait"; - }]; - }; "packed_simd" = rec { crateName = "packed_simd"; @@ -7524,30 +7339,6 @@ rec { }; resolvedDefaultFeatures = [ "default" ]; }; - "parking_lot 0.4.8" = rec { - crateName = "parking_lot"; - version = "0.4.8"; - edition = "2015"; - sha256 = "0ph0kv3dfcxpjbi83wkzammqb7lm95j8in7w7hz17hgkjxdqz78l"; - authors = [ "Amanieu d'Antras " ]; - dependencies = [ - { - name = "owning_ref"; - packageId = "owning_ref"; - optional = true; - } - { - name = "parking_lot_core"; - packageId = "parking_lot_core 0.2.14"; - } - ]; - features = { - "deadlock_detection" = [ "parking_lot_core/deadlock_detection" ]; - "default" = [ "owning_ref" ]; - "nightly" = [ "parking_lot_core/nightly" ]; - }; - resolvedDefaultFeatures = [ "default" "owning_ref" ]; - }; "parking_lot 0.9.0" = rec { crateName = "parking_lot"; version = "0.9.0"; @@ -7576,45 +7367,6 @@ rec { }; resolvedDefaultFeatures = [ "default" ]; }; - "parking_lot_core 0.2.14" = rec { - crateName = "parking_lot_core"; - version = "0.2.14"; - edition = "2015"; - sha256 = "1yip8m6npxb87ilnn0q774psp1zd0vgv66fcjkkvr9rlyz6aicad"; - authors = [ "Amanieu d'Antras " ]; - dependencies = [ - { - name = "libc"; - packageId = "libc"; - target = { target, features }: target."unix"; - } - { - name = "rand"; - packageId = "rand 0.4.6"; - } - { - name = "smallvec"; - packageId = "smallvec 0.6.13"; - } - { - name = "winapi"; - packageId = "winapi 0.3.8"; - target = { target, features }: target."windows"; - features = [ - "winnt" - "ntstatus" - "minwindef" - "winerror" - "winbase" - "errhandlingapi" - "handleapi" - ]; - } - ]; - features = { - "deadlock_detection" = [ "petgraph" "thread-id" "backtrace" ]; - }; - }; "parking_lot_core 0.6.2" = rec { crateName = "parking_lot_core"; version = "0.6.2"; @@ -7792,7 +7544,7 @@ rec { } { name = "syn"; - packageId = "syn 1.0.31"; + packageId = "syn 1.0.33"; features = [ "full" "visit-mut" ]; } ]; @@ -7995,28 +7747,6 @@ rec { features = { "default" = [ "std" ]; }; resolvedDefaultFeatures = [ "simd" "std" ]; }; - "pretty_env_logger" = rec { - crateName = "pretty_env_logger"; - version = "0.3.1"; - edition = "2015"; - sha256 = "0x4hyjlnvvhyk9m74iypzybm22w3dl2k8img4b956239n5vf8zki"; - authors = [ "Sean McArthur " ]; - dependencies = [ - { - name = "chrono"; - packageId = "chrono"; - } - { - name = "env_logger"; - packageId = "env_logger"; - } - { - name = "log"; - packageId = "log"; - } - ]; - - }; "proc-macro-hack" = rec { crateName = "proc-macro-hack"; version = "0.5.16"; @@ -8034,22 +7764,6 @@ rec { authors = [ "David Tolnay " ]; }; - "proc-macro2 0.3.8" = rec { - crateName = "proc-macro2"; - version = "0.3.8"; - edition = "2015"; - sha256 = "1ryaynnaj39l4zphcg5w8wszndd80vsrv89m5d2293gl6pry41hv"; - authors = [ "Alex Crichton " ]; - dependencies = [{ - name = "unicode-xid"; - packageId = "unicode-xid 0.1.0"; - }]; - features = { - "default" = [ "proc-macro" ]; - "nightly" = [ "proc-macro" ]; - }; - resolvedDefaultFeatures = [ "default" "proc-macro" ]; - }; "proc-macro2 0.4.30" = rec { crateName = "proc-macro2"; version = "0.4.30"; @@ -8081,9 +7795,9 @@ rec { }; "prometheus" = rec { crateName = "prometheus"; - version = "0.7.0"; + version = "0.9.0"; edition = "2018"; - sha256 = "1wcafa78459f33x31081zi8yfacgalfgz40vbd2wdqkqaxnlhrsm"; + sha256 = "1k8ig1yayxvl96kavsmjdmc19mj9i324viqm1jb6x6p3vrbfs36x"; authors = [ "overvenus@gmail.com" "siddontang@gmail.com" "vistaswx@gmail.com" ]; dependencies = [ @@ -8099,52 +7813,23 @@ rec { name = "lazy_static"; packageId = "lazy_static"; } - { - name = "quick-error"; - packageId = "quick-error"; - } { name = "spin"; packageId = "spin"; } + { + name = "thiserror"; + packageId = "thiserror"; + } ]; features = { "default" = [ "protobuf" ]; "gen" = [ "protobuf-codegen-pure" ]; "nightly" = [ "libc" ]; - "process" = [ "libc" "procinfo" ]; - "push" = [ "reqwest" "libc" ]; + "process" = [ "libc" "procfs" ]; + "push" = [ "reqwest" "libc" "protobuf" ]; }; }; - "prometheus-static-metric" = rec { - crateName = "prometheus-static-metric"; - version = "0.2.0"; - edition = "2015"; - sha256 = "04v06wy1pg1j5dsn8phsyny2wq1zk8lp6kr0hcvzgkr36m0mgahv"; - procMacro = true; - authors = [ "me@breeswish.org" ]; - dependencies = [ - { - name = "lazy_static"; - packageId = "lazy_static"; - } - { - name = "proc-macro2"; - packageId = "proc-macro2 0.3.8"; - } - { - name = "quote"; - packageId = "quote 0.5.2"; - } - { - name = "syn"; - packageId = "syn 0.13.11"; - features = [ "full" "extra-traits" ]; - } - ]; - features = { }; - resolvedDefaultFeatures = [ "default" ]; - }; "qstring" = rec { crateName = "qstring"; version = "0.7.2"; @@ -8168,23 +7853,6 @@ rec { ]; }; - "quote 0.5.2" = rec { - crateName = "quote"; - version = "0.5.2"; - edition = "2015"; - sha256 = "1s01fh0jl8qv4xggs85yahw0h507nzrxkjbf7vay3zw8d3kcyjcr"; - authors = [ "David Tolnay " ]; - dependencies = [{ - name = "proc-macro2"; - packageId = "proc-macro2 0.3.8"; - usesDefaultFeatures = false; - }]; - features = { - "default" = [ "proc-macro" ]; - "proc-macro" = [ "proc-macro2/proc-macro" ]; - }; - resolvedDefaultFeatures = [ "default" "proc-macro" ]; - }; "quote 0.6.13" = rec { crateName = "quote"; version = "0.6.13"; @@ -8230,49 +7898,6 @@ rec { ]; }; - "rand 0.4.6" = rec { - crateName = "rand"; - version = "0.4.6"; - edition = "2015"; - sha256 = "14qjfv3gggzhnma20k0sc1jf8y6pplsaq7n1j9ls5c8kf2wl0a2m"; - authors = [ "The Rust Project Developers" ]; - dependencies = [ - { - name = "fuchsia-cprng"; - packageId = "fuchsia-cprng"; - target = { target, features }: (target."os" == "fuchsia"); - } - { - name = "libc"; - packageId = "libc"; - optional = true; - target = { target, features }: target."unix"; - } - { - name = "rand_core"; - packageId = "rand_core 0.3.1"; - usesDefaultFeatures = false; - target = { target, features }: (target."env" == "sgx"); - } - { - name = "rdrand"; - packageId = "rdrand"; - target = { target, features }: (target."env" == "sgx"); - } - { - name = "winapi"; - packageId = "winapi 0.3.8"; - target = { target, features }: target."windows"; - features = [ "minwindef" "ntsecapi" "profileapi" "winnt" ]; - } - ]; - features = { - "default" = [ "std" ]; - "nightly" = [ "i128_support" ]; - "std" = [ "libc" ]; - }; - resolvedDefaultFeatures = [ "default" "libc" "std" ]; - }; "rand 0.5.6" = rec { crateName = "rand"; version = "0.5.6"; @@ -9053,9 +8678,9 @@ rec { }; "rodio" = rec { crateName = "rodio"; - version = "0.10.0"; + version = "0.11.0"; edition = "2015"; - sha256 = "0qkjnybcxrsrdcdijiprhv624narx8i2m6bfix1cc5wbgkx0s3hy"; + sha256 = "0w3s45pay3q618apgc8yj9z15h951mz85mhpg8dm1m9g4rhg5fvk"; authors = [ "Pierre Krieger " ]; dependencies = [ { @@ -9533,9 +9158,9 @@ rec { }; "serde" = rec { crateName = "serde"; - version = "1.0.111"; + version = "1.0.114"; edition = "2015"; - sha256 = "0zg3i95c663yqyfmqpwr1l3s6h60jjw6mk5jh003ig8cnksls4n9"; + sha256 = "1lwcxlh8c09cs6qmwr6w68hl989mczwmwrzgc3p7hl0aixcgf5sk"; authors = [ "Erick Tryzelaar " "David Tolnay " @@ -9557,9 +9182,9 @@ rec { }; "serde_derive" = rec { crateName = "serde_derive"; - version = "1.0.111"; + version = "1.0.114"; edition = "2015"; - sha256 = "0l5j3v7g4lj18w4iifzz7jp83gv206a2645yp209q7nawv43lb1z"; + sha256 = "13lgjxsc617yhblm779jwg43gxab2dfgrpyd6znvl3v90i5yj2ra"; procMacro = true; authors = [ "Erick Tryzelaar " @@ -9576,7 +9201,7 @@ rec { } { name = "syn"; - packageId = "syn 1.0.31"; + packageId = "syn 1.0.33"; features = [ "visit" ]; } ]; @@ -9691,14 +9316,14 @@ rec { }; "shred" = rec { crateName = "shred"; - version = "0.9.4"; + version = "0.10.2"; edition = "2018"; - sha256 = "1k3czyp7xaa99984k33pyr12fcz54kcl8r2wjchvmg5gmydjniwj"; + sha256 = "0v81g5vwa8bwqm388hiinb6966nr8dcqp1zq42nr9b37wqvq5w65"; authors = [ "torkleyy " ]; dependencies = [ { name = "arrayvec"; - packageId = "arrayvec 0.4.12"; + packageId = "arrayvec 0.5.1"; } { name = "hashbrown"; @@ -9720,7 +9345,11 @@ rec { } { name = "smallvec"; - packageId = "smallvec 0.6.13"; + packageId = "smallvec 1.4.0"; + } + { + name = "tynm"; + packageId = "tynm"; } ]; devDependencies = [{ @@ -9731,8 +9360,7 @@ rec { "default" = [ "parallel" "shred-derive" ]; "parallel" = [ "rayon" ]; }; - resolvedDefaultFeatures = - [ "nightly" "parallel" "rayon" "shred-derive" ]; + resolvedDefaultFeatures = [ "parallel" "rayon" "shred-derive" ]; }; "shred-derive" = rec { crateName = "shred-derive"; @@ -9752,7 +9380,7 @@ rec { } { name = "syn"; - packageId = "syn 1.0.31"; + packageId = "syn 1.0.33"; } ]; @@ -9925,18 +9553,19 @@ rec { }; "specs" = rec { crateName = "specs"; - version = "0.15.1"; + version = "0.16.1"; edition = "2018"; - sha256 = "1339xf7s95124lswvrhr18knqqkvpnmwg96j34ylrlfkqplgshs9"; + workspace_member = null; + src = pkgs.fetchgit { + url = "https://github.com/amethyst/specs.git"; + rev = "7a2e348ab2223818bad487695c66c43db88050a5"; + sha256 = "1z7gjiq7zirg9az3ly1y2ghi5m7s3x1bp35gw5x0cyv50fsi3pjq"; + }; authors = [ "slide-rs hackers" ]; dependencies = [ { name = "crossbeam-queue"; - packageId = "crossbeam-queue 0.1.2"; - } - { - name = "derivative"; - packageId = "derivative"; + packageId = "crossbeam-queue 0.2.3"; } { name = "hashbrown"; @@ -9979,11 +9608,12 @@ rec { devDependencies = [{ name = "shred"; packageId = "shred"; + usesDefaultFeatures = false; features = [ "shred-derive" ]; }]; features = { "default" = [ "parallel" ]; - "nightly" = [ "shred/nightly" ]; + "derive" = [ "shred-derive" "specs-derive" ]; "parallel" = [ "rayon" "shred/parallel" "hibitset/parallel" ]; "shred-derive" = [ "shred/shred-derive" ]; "stdweb" = [ "uuid/stdweb" ]; @@ -9992,7 +9622,6 @@ rec { }; resolvedDefaultFeatures = [ "default" - "nightly" "parallel" "rayon" "serde" @@ -10007,8 +9636,8 @@ rec { workspace_member = null; src = pkgs.fetchgit { url = "https://gitlab.com/veloren/specs-idvs.git"; - rev = "111548cf44e9bb4c43feb12aa3fc253953ac6e4a"; - sha256 = "1qgbzr4g4iys10hi56l6z7k3gk9qwj9xamfdigqnkhxvcynfhqqv"; + rev = "fcb0b2306b571f62f9f85d89e79e087454d95efd"; + sha256 = "00w4kc60d7mjb5gbkcxrxzarshmhf4idqm3sk8335f7s3pn9q0s5"; }; authors = [ "Acrimon " ]; dependencies = [{ @@ -10028,15 +9657,6 @@ rec { ]; }; - "stable_deref_trait" = rec { - crateName = "stable_deref_trait"; - version = "1.1.1"; - edition = "2015"; - sha256 = "1j2lkgakksmz4vc5hfawcch2ipiskrhjs1sih0f3br7s7rys58fv"; - authors = [ "Robert Grosse " ]; - features = { "default" = [ "std" ]; }; - resolvedDefaultFeatures = [ "default" "std" ]; - }; "static_assertions" = rec { crateName = "static_assertions"; version = "1.1.0"; @@ -10162,7 +9782,7 @@ rec { } { name = "syn"; - packageId = "syn 1.0.31"; + packageId = "syn 1.0.33"; usesDefaultFeatures = false; features = [ "derive" "parsing" "printing" ]; } @@ -10207,7 +9827,7 @@ rec { } { name = "syn"; - packageId = "syn 1.0.31"; + packageId = "syn 1.0.33"; usesDefaultFeatures = false; features = [ "full" "parsing" "printing" "clone-impls" ]; } @@ -10262,47 +9882,6 @@ rec { authors = [ "Nicolas Silva " ]; }; - "syn 0.13.11" = rec { - crateName = "syn"; - version = "0.13.11"; - edition = "2015"; - sha256 = "16qvx8qyb5v4vjbg9rk8848bw6x4i6vzs8v7f4n1v9pkj9ibzy8l"; - authors = [ "David Tolnay " ]; - dependencies = [ - { - name = "proc-macro2"; - packageId = "proc-macro2 0.3.8"; - usesDefaultFeatures = false; - } - { - name = "quote"; - packageId = "quote 0.5.2"; - optional = true; - usesDefaultFeatures = false; - } - { - name = "unicode-xid"; - packageId = "unicode-xid 0.1.0"; - } - ]; - features = { - "default" = - [ "derive" "parsing" "printing" "clone-impls" "proc-macro" ]; - "printing" = [ "quote" ]; - "proc-macro" = [ "proc-macro2/proc-macro" "quote/proc-macro" ]; - }; - resolvedDefaultFeatures = [ - "clone-impls" - "default" - "derive" - "extra-traits" - "full" - "parsing" - "printing" - "proc-macro" - "quote" - ]; - }; "syn 0.15.44" = rec { crateName = "syn"; version = "0.15.44"; @@ -10345,11 +9924,11 @@ rec { "visit" ]; }; - "syn 1.0.31" = rec { + "syn 1.0.33" = rec { crateName = "syn"; - version = "1.0.31"; + version = "1.0.33"; edition = "2018"; - sha256 = "1dm8vd1kn2f7i7nprwc8xwhys5jhnf8szm15bicbfrbkybylqc5m"; + sha256 = "1kdj0piws00cc0rgn2315625dfxfpxrzf6gib5lms05viipdkmg8"; authors = [ "David Tolnay " ]; dependencies = [ { @@ -10409,7 +9988,7 @@ rec { } { name = "syn"; - packageId = "syn 1.0.31"; + packageId = "syn 1.0.33"; usesDefaultFeatures = false; features = [ "derive" @@ -10432,19 +10011,6 @@ rec { }; resolvedDefaultFeatures = [ "default" "proc-macro" ]; }; - "termcolor" = rec { - crateName = "termcolor"; - version = "1.1.0"; - edition = "2018"; - sha256 = "0pyp8vc0gx7124y80ixdl6plbfn1yjhw04i875k5fz2dk8lglsxv"; - authors = [ "Andrew Gallant " ]; - dependencies = [{ - name = "winapi-util"; - packageId = "winapi-util"; - target = { target, features }: target."windows"; - }]; - - }; "textwrap" = rec { crateName = "textwrap"; version = "0.11.0"; @@ -10456,6 +10022,41 @@ rec { packageId = "unicode-width"; }]; + }; + "thiserror" = rec { + crateName = "thiserror"; + version = "1.0.20"; + edition = "2018"; + sha256 = "020d7pfq7vg2iw1bbilnyq764zy35ncg2syn9a7vgk6qriqd1zbx"; + authors = [ "David Tolnay " ]; + dependencies = [{ + name = "thiserror-impl"; + packageId = "thiserror-impl"; + }]; + + }; + "thiserror-impl" = rec { + crateName = "thiserror-impl"; + version = "1.0.20"; + edition = "2018"; + sha256 = "14qphjwa68sfjk3404iwv6hh8kvk6vmcwan9589sqqrhyw9gr05x"; + procMacro = true; + authors = [ "David Tolnay " ]; + dependencies = [ + { + name = "proc-macro2"; + packageId = "proc-macro2 1.0.18"; + } + { + name = "quote"; + packageId = "quote 1.0.7"; + } + { + name = "syn"; + packageId = "syn 1.0.33"; + } + ]; + }; "thread_local" = rec { crateName = "thread_local"; @@ -10642,6 +10243,15 @@ rec { ]; }; + "tinyvec" = rec { + crateName = "tinyvec"; + version = "0.3.3"; + edition = "2018"; + sha256 = "1vgg2z317kq75bpd0nfda2v507qjpd7g2cjahjgivn2s78nkv5ak"; + authors = [ "Lokathor " ]; + features = { }; + resolvedDefaultFeatures = [ "alloc" "default" ]; + }; "tokio" = rec { crateName = "tokio"; version = "0.1.22"; @@ -11045,6 +10655,11 @@ rec { name = "cfg-if"; packageId = "cfg-if"; } + { + name = "tracing-attributes"; + packageId = "tracing-attributes"; + optional = true; + } { name = "tracing-core"; packageId = "tracing-core"; @@ -11057,7 +10672,61 @@ rec { "log-always" = [ "log" ]; "std" = [ "tracing-core/std" ]; }; - resolvedDefaultFeatures = [ "std" ]; + resolvedDefaultFeatures = + [ "attributes" "default" "std" "tracing-attributes" ]; + }; + "tracing-appender" = rec { + crateName = "tracing-appender"; + version = "0.1.0"; + edition = "2018"; + sha256 = "1h19cskjxvzpv685ybb0vsfn6r585mzz4b7hzm7z8m4vyqgsd1hy"; + authors = [ + "Zeki Sherif " + "Tokio Contributors " + ]; + dependencies = [ + { + name = "chrono"; + packageId = "chrono"; + } + { + name = "crossbeam-channel"; + packageId = "crossbeam-channel 0.4.2"; + } + { + name = "tracing-subscriber"; + packageId = "tracing-subscriber"; + } + ]; + + }; + "tracing-attributes" = rec { + crateName = "tracing-attributes"; + version = "0.1.8"; + edition = "2018"; + sha256 = "0k4qvq437md3zynm8qbas6jfb0xp222xisij6af3r4pxwc6svfwr"; + procMacro = true; + authors = [ + "Tokio Contributors " + "Eliza Weisman " + "David Barsky " + ]; + dependencies = [ + { + name = "proc-macro2"; + packageId = "proc-macro2 1.0.18"; + } + { + name = "quote"; + packageId = "quote 1.0.7"; + } + { + name = "syn"; + packageId = "syn 1.0.33"; + features = [ "full" "extra-traits" ]; + } + ]; + features = { }; }; "tracing-core" = rec { crateName = "tracing-core"; @@ -11107,11 +10776,56 @@ rec { resolvedDefaultFeatures = [ "default" "pin-project" "std" "std-future" ]; }; + "tracing-log" = rec { + crateName = "tracing-log"; + version = "0.1.1"; + edition = "2018"; + sha256 = "1fdr0az98q9m5kiybvdvsb2m9mg86fdidgb5czzq2d71g1qqq3sy"; + authors = [ "Tokio Contributors " ]; + dependencies = [ + { + name = "lazy_static"; + packageId = "lazy_static"; + } + { + name = "log"; + packageId = "log"; + } + { + name = "tracing-core"; + packageId = "tracing-core"; + } + ]; + features = { + "default" = [ "log-tracer" "trace-logger" "std" ]; + "std" = [ "log/std" ]; + }; + resolvedDefaultFeatures = + [ "default" "log-tracer" "std" "trace-logger" ]; + }; + "tracing-serde" = rec { + crateName = "tracing-serde"; + version = "0.1.1"; + edition = "2018"; + sha256 = "0ybfv0bzx542xkqzhcjx33knbs92zyvxjrf7iwkfvq0niwpvmk5n"; + authors = [ "Tokio Contributors " ]; + dependencies = [ + { + name = "serde"; + packageId = "serde"; + } + { + name = "tracing-core"; + packageId = "tracing-core"; + } + ]; + + }; "tracing-subscriber" = rec { crateName = "tracing-subscriber"; - version = "0.2.5"; + version = "0.2.6"; edition = "2018"; - sha256 = "1li6g1hw2q3wz39c5g9kvw1q9jl6d3r87x0zsapcjsdai42c8lqx"; + sha256 = "06d1lb92zknb4isd1xc993a12ahlykzbm05mw7v8zqq9j52ip884"; authors = [ "Eliza Weisman " "David Barsky " @@ -11143,6 +10857,16 @@ rec { packageId = "regex"; optional = true; } + { + name = "serde"; + packageId = "serde"; + optional = true; + } + { + name = "serde_json"; + packageId = "serde_json"; + optional = true; + } { name = "sharded-slab"; packageId = "sharded-slab"; @@ -11157,7 +10881,23 @@ rec { name = "tracing-core"; packageId = "tracing-core"; } + { + name = "tracing-log"; + packageId = "tracing-log"; + optional = true; + usesDefaultFeatures = false; + features = [ "log-tracer" "std" ]; + } + { + name = "tracing-serde"; + packageId = "tracing-serde"; + optional = true; + } ]; + devDependencies = [{ + name = "tracing-log"; + packageId = "tracing-log"; + }]; features = { "ansi" = [ "fmt" "ansi_term" ]; "default" = [ @@ -11178,14 +10918,20 @@ rec { "ansi" "ansi_term" "chrono" + "default" "env-filter" "fmt" + "json" "lazy_static" "matchers" "regex" "registry" + "serde" + "serde_json" "sharded-slab" "smallvec" + "tracing-log" + "tracing-serde" ]; }; "treeculler" = rec { @@ -11226,6 +10972,20 @@ rec { sha256 = "1wgl7a32a9gvcxiyxznmglc9kc4d2j7c4dfzpr3nzcf5w8c490s4"; authors = [ "Colin Sherratt " ]; + }; + "tynm" = rec { + crateName = "tynm"; + version = "0.1.4"; + edition = "2018"; + sha256 = "0knk747j6hl2fw2i008z135sxjki315waqik1alv8q9rjs0vfzrn"; + authors = [ "Azriel Hoh " ]; + dependencies = [{ + name = "nom"; + packageId = "nom 5.1.1"; + usesDefaultFeatures = false; + features = [ "std" ]; + }]; + }; "unicode-bidi" = rec { crateName = "unicode-bidi"; @@ -11594,12 +11354,15 @@ rec { authors = [ "Joshua Barretto " ]; dependencies = [ { - name = "log"; - packageId = "log"; + name = "tracing"; + packageId = "tracing"; + usesDefaultFeatures = false; } { - name = "pretty_env_logger"; - packageId = "pretty_env_logger"; + name = "tracing-subscriber"; + packageId = "tracing-subscriber"; + usesDefaultFeatures = false; + features = [ "fmt" "chrono" "ansi" "smallvec" ]; } { name = "veloren-client"; @@ -11632,6 +11395,18 @@ rec { name = "byteorder"; packageId = "byteorder 1.3.4"; } + { + name = "futures-executor"; + packageId = "futures-executor"; + } + { + name = "futures-timer"; + packageId = "futures-timer"; + } + { + name = "futures-util"; + packageId = "futures-util"; + } { name = "hashbrown"; packageId = "hashbrown"; @@ -11643,10 +11418,6 @@ rec { usesDefaultFeatures = false; features = [ "png" ]; } - { - name = "log"; - packageId = "log"; - } { name = "num_cpus"; packageId = "num_cpus"; @@ -11655,6 +11426,11 @@ rec { name = "specs"; packageId = "specs"; } + { + name = "tracing"; + packageId = "tracing"; + usesDefaultFeatures = false; + } { name = "uvth"; packageId = "uvth 3.1.1"; @@ -11670,6 +11446,12 @@ rec { rename = "common"; features = [ "no-assets" ]; } + { + name = "veloren_network"; + packageId = "veloren_network"; + rename = "network"; + usesDefaultFeatures = false; + } ]; }; @@ -11691,10 +11473,6 @@ rec { name = "authc"; packageId = "authc"; } - { - name = "bincode"; - packageId = "bincode"; - } { name = "crossbeam"; packageId = "crossbeam"; @@ -11726,18 +11504,6 @@ rec { name = "lazy_static"; packageId = "lazy_static"; } - { - name = "log"; - packageId = "log"; - } - { - name = "lz4-compress"; - packageId = "lz4-compress"; - } - { - name = "mio"; - packageId = "mio"; - } { name = "notify"; packageId = "notify"; @@ -11762,10 +11528,7 @@ rec { { name = "serde"; packageId = "serde"; - } - { - name = "serde_derive"; - packageId = "serde_derive"; + features = [ "derive" ]; } { name = "serde_json"; @@ -11774,7 +11537,7 @@ rec { { name = "specs"; packageId = "specs"; - features = [ "serde" "nightly" "storage-event-control" ]; + features = [ "serde" "storage-event-control" ]; } { name = "specs-idvs"; @@ -11784,6 +11547,11 @@ rec { name = "sum_type"; packageId = "sum_type"; } + { + name = "tracing"; + packageId = "tracing"; + usesDefaultFeatures = false; + } { name = "vek"; packageId = "vek"; @@ -11832,6 +11600,18 @@ rec { name = "dotenv"; packageId = "dotenv"; } + { + name = "futures-executor"; + packageId = "futures-executor"; + } + { + name = "futures-timer"; + packageId = "futures-timer"; + } + { + name = "futures-util"; + packageId = "futures-util"; + } { name = "hashbrown"; packageId = "hashbrown"; @@ -11846,10 +11626,6 @@ rec { packageId = "libsqlite3-sys"; features = [ "bundled" ]; } - { - name = "log"; - packageId = "log"; - } { name = "portpicker"; packageId = "portpicker"; @@ -11859,10 +11635,6 @@ rec { packageId = "prometheus"; usesDefaultFeatures = false; } - { - name = "prometheus-static-metric"; - packageId = "prometheus-static-metric"; - } { name = "rand"; packageId = "rand 0.7.3"; @@ -11880,10 +11652,7 @@ rec { { name = "serde"; packageId = "serde"; - } - { - name = "serde_derive"; - packageId = "serde_derive"; + features = [ "derive" ]; } { name = "serde_json"; @@ -11902,6 +11671,10 @@ rec { name = "tiny_http"; packageId = "tiny_http"; } + { + name = "tracing"; + packageId = "tracing"; + } { name = "uvth"; packageId = "uvth 3.1.1"; @@ -11920,6 +11693,12 @@ rec { packageId = "veloren-world"; rename = "world"; } + { + name = "veloren_network"; + packageId = "veloren_network"; + rename = "network"; + usesDefaultFeatures = false; + } ]; features = { "default" = [ "worldgen" ]; }; resolvedDefaultFeatures = [ "default" "worldgen" ]; @@ -11939,12 +11718,15 @@ rec { authors = [ "Joshua Barretto " ]; dependencies = [ { - name = "log"; - packageId = "log"; + name = "tracing"; + packageId = "tracing"; + usesDefaultFeatures = false; } { - name = "pretty_env_logger"; - packageId = "pretty_env_logger"; + name = "tracing-subscriber"; + packageId = "tracing-subscriber"; + usesDefaultFeatures = false; + features = [ "env-filter" "fmt" "chrono" "ansi" "smallvec" ]; } { name = "veloren-common"; @@ -12023,8 +11805,8 @@ rec { packageId = "deunicode"; } { - name = "directories"; - packageId = "directories"; + name = "directories-next"; + packageId = "directories-next"; } { name = "dispatch"; @@ -12043,11 +11825,6 @@ rec { name = "failure"; packageId = "failure"; } - { - name = "fern"; - packageId = "fern"; - features = [ "colored" ]; - } { name = "gfx"; packageId = "gfx"; @@ -12089,10 +11866,6 @@ rec { usesDefaultFeatures = false; features = [ "ico" "png" ]; } - { - name = "log"; - packageId = "log"; - } { name = "msgbox"; packageId = "msgbox"; @@ -12134,6 +11907,25 @@ rec { name = "specs-idvs"; packageId = "specs-idvs"; } + { + name = "tracing"; + packageId = "tracing"; + } + { + name = "tracing-appender"; + packageId = "tracing-appender"; + } + { + name = "tracing-log"; + packageId = "tracing-log"; + } + { + name = "tracing-subscriber"; + packageId = "tracing-subscriber"; + usesDefaultFeatures = false; + features = + [ "env-filter" "fmt" "chrono" "ansi" "smallvec" "tracing-log" ]; + } { name = "treeculler"; packageId = "treeculler"; @@ -12232,6 +12024,11 @@ rec { "Imbris " ]; dependencies = [ + { + name = "find_folder"; + packageId = "find_folder"; + optional = true; + } { name = "lazy_static"; packageId = "lazy_static"; @@ -12243,13 +12040,13 @@ rec { optional = true; } { - name = "log"; - packageId = "log"; + name = "notify"; + packageId = "notify"; optional = true; } { - name = "notify"; - packageId = "notify"; + name = "tracing"; + packageId = "tracing"; optional = true; } { @@ -12265,15 +12062,17 @@ rec { ]; features = { "default" = [ "be-dyn-lib" ]; - "use-dyn-lib" = [ "libloading" "notify" "lazy_static" "log" ]; + "use-dyn-lib" = + [ "libloading" "notify" "lazy_static" "tracing" "find_folder" ]; }; resolvedDefaultFeatures = [ "be-dyn-lib" "default" + "find_folder" "lazy_static" "libloading" - "log" "notify" + "tracing" "use-dyn-lib" ]; }; @@ -12316,16 +12115,12 @@ rec { } { name = "itertools"; - packageId = "itertools 0.8.2"; + packageId = "itertools"; } { name = "lazy_static"; packageId = "lazy_static"; } - { - name = "log"; - packageId = "log"; - } { name = "noise"; packageId = "noise"; @@ -12367,10 +12162,12 @@ rec { { name = "serde"; packageId = "serde"; + features = [ "derive" ]; } { - name = "serde_derive"; - packageId = "serde_derive"; + name = "tracing"; + packageId = "tracing"; + usesDefaultFeatures = false; } { name = "vek"; @@ -12388,8 +12185,10 @@ rec { packageId = "minifb"; } { - name = "pretty_env_logger"; - packageId = "pretty_env_logger"; + name = "tracing-subscriber"; + packageId = "tracing-subscriber"; + usesDefaultFeatures = false; + features = [ "fmt" "chrono" "ansi" "smallvec" ]; } ]; @@ -12414,6 +12213,10 @@ rec { name = "bincode"; packageId = "bincode"; } + { + name = "crossbeam-channel"; + packageId = "crossbeam-channel 0.4.2"; + } { name = "futures"; packageId = "futures 0.3.5"; @@ -12424,6 +12227,10 @@ rec { packageId = "lazy_static"; usesDefaultFeatures = false; } + { + name = "lz4-compress"; + packageId = "lz4-compress"; + } { name = "prometheus"; packageId = "prometheus"; @@ -12603,7 +12410,7 @@ rec { } { name = "syn"; - packageId = "syn 1.0.31"; + packageId = "syn 1.0.33"; features = [ "full" ]; } { @@ -12654,7 +12461,7 @@ rec { } { name = "syn"; - packageId = "syn 1.0.31"; + packageId = "syn 1.0.33"; features = [ "visit" ]; } { diff --git a/nix/crate-hashes.json b/nix/crate-hashes.json index 4c18b873ce..423321de95 100644 --- a/nix/crate-hashes.json +++ b/nix/crate-hashes.json @@ -8,6 +8,7 @@ "guillotiere 0.4.2 (git+https://github.com/Imberflur/guillotiere#42c298f5bcf0f95f1a004360d05e25ca3711e9ed)": "1knqbn777f3cgzbsaqawzclgrqf3nav6x3gjzc2hsls3acccx1ya", "msgbox 0.4.0 (git+https://github.com/bekker/msgbox-rs.git?rev=68fe39a#68fe39a60019b38a1569ae4e9ed796a0f0542673)": "18h6s50n7mz2ggfishhi91p2298shqhzx5xagpg9q4zb4y90c2wr", "portpicker 0.1.0 (git+https://github.com/xMAC94x/portpicker-rs#9d6df36c53c94684080a64a7212dd6bfc3617ee4)": "00vl2k3xfihxq86kf5rsknjl8dxmrxqhwajwn0hj4gzgnbssr0rx", - "specs-idvs 0.1.0 (git+https://gitlab.com/veloren/specs-idvs.git#111548cf44e9bb4c43feb12aa3fc253953ac6e4a)": "1qgbzr4g4iys10hi56l6z7k3gk9qwj9xamfdigqnkhxvcynfhqqv", + "specs 0.16.1 (git+https://github.com/amethyst/specs.git?rev=7a2e348ab2223818bad487695c66c43db88050a5#7a2e348ab2223818bad487695c66c43db88050a5)": "1z7gjiq7zirg9az3ly1y2ghi5m7s3x1bp35gw5x0cyv50fsi3pjq", + "specs-idvs 0.1.0 (git+https://gitlab.com/veloren/specs-idvs.git?branch=specs-git#fcb0b2306b571f62f9f85d89e79e087454d95efd)": "00w4kc60d7mjb5gbkcxrxzarshmhf4idqm3sk8335f7s3pn9q0s5", "treeculler 0.1.0 (git+https://gitlab.com/yusdacra/treeculler.git#efcf5283cf386117a7e654abdaa45ef664a08e42)": "19niwgha0jnvrp22pq0070dfimb2wkda53a3parhga3vhap2g01b" } \ No newline at end of file diff --git a/nix/default.nix b/nix/default.nix index 7814f52e37..0d071cac35 100644 --- a/nix/default.nix +++ b/nix/default.nix @@ -1,12 +1,23 @@ { crateName ? "veloren-voxygen", # `crate2nix` doesn't support profiles in `Cargo.toml`, so default to release. Otherwise bad performance (non-release is built with opt level 0) -release ? true, sources ? import ./sources.nix { }, nixpkgsSrc ? }: +release ? true, nixpkgs ? , system ? builtins.currentSystem +, sources ? import ./sources.nix { inherit system; } }: let - # Check if git-lfs is working. - isGitLfsSetup = + gitHash = + # Check if git-lfs is working. if builtins.pathExists ../assets/voxygen/background/bg_main.png then - true + builtins.readFile (pkgs.runCommand "getGitHash" { } '' + cd ${ + # Only copy the `.git` directory to nix store, anything else is a waste. + builtins.path { + path = ../.git; + # Nix store path names don't accept names that start with a dot. + name = "git"; + } + } + ${pkgs.git}/bin/git log -n 1 --pretty=format:%h/%cd --date=format:%Y-%m-%d-%H:%M --abbrev=8 > $out + '') else abort '' Git Large File Storage (git-lfs) has not been set up correctly. @@ -17,34 +28,17 @@ let See the book at https://book.veloren.net/ for details. ''; - pkgs = import ./nixpkgs.nix { inherit sources nixpkgsSrc; }; - - # Only copy the `.git` directory to nix store, anything else is a waste. - gitSrc = builtins.path { - path = ../.git; - name = "git"; - }; - gitHash = builtins.readFile (with pkgs; - runCommand "getGitHash" { nativeBuildInputs = [ git ]; } '' - cd ${gitSrc} - git log -n 1 --pretty=format:%h/%cd --date=format:%Y-%m-%d-%H:%M --abbrev=8 > $out - ''); + pkgs = import ./nixpkgs.nix { inherit sources nixpkgs system; }; veloren = with pkgs; callPackage ./Cargo.nix { defaultCrateOverrides = defaultCrateOverrides // { - libudev-sys = attrs: { buildInputs = [ pkg-config libudev ]; }; - alsa-sys = attrs: { buildInputs = [ pkg-config alsaLib ]; }; - veloren-common = attrs: { - NIX_GIT_HASH = gitHash; - # We need to include the result here otherwise nix won't evaluate the check. - GIT_LFS_SETUP = isGitLfsSetup; - }; - veloren-network = attrs: { buildInputs = [ pkg-config openssl ]; }; - veloren-voxygen = attrs: { - buildInputs = [ atk cairo glib gtk3 pango ]; - }; + libudev-sys = _: { buildInputs = [ pkg-config libudev ]; }; + alsa-sys = _: { buildInputs = [ pkg-config alsaLib ]; }; + veloren-common = _: { NIX_GIT_HASH = gitHash; }; + veloren-network = _: { buildInputs = [ pkg-config openssl ]; }; + veloren-voxygen = _: { buildInputs = [ atk cairo glib gtk3 pango ]; }; }; - inherit release pkgs; + inherit release pkgs nixpkgs; }; in veloren.workspaceMembers."${crateName}".build diff --git a/nix/nixpkgs.nix b/nix/nixpkgs.nix index de8cac46ee..a4aae61026 100644 --- a/nix/nixpkgs.nix +++ b/nix/nixpkgs.nix @@ -1,11 +1,16 @@ -{ sources ? import ./sources.nix { }, nixpkgsSrc ? }: +{ nixpkgs ? , system ? builtins.currentSystem +, sources ? import ./sources.nix { inherit system; } }: let mozPkgs = import "${sources.nixpkgsMoz}/package-set.nix" { - pkgs = import nixpkgsSrc { }; + pkgs = import nixpkgs { inherit system; }; }; - rustChannel = mozPkgs.rustChannelOf { rustToolchain = ../rust-toolchain; }; -in import nixpkgsSrc { + rustChannel = mozPkgs.rustChannelOf { + rustToolchain = ../rust-toolchain; + sha256 = "sha256-18R7sZfLGmtYkz24jUaq268fJO2A71p+dWvGm4DgqEw="; + }; +in import nixpkgs { + inherit system; overlays = [ (self: super: { rustc = rustChannel.rust; diff --git a/nix/shell.nix b/nix/shell.nix index b2f2b67959..ec3b89aea3 100644 --- a/nix/shell.nix +++ b/nix/shell.nix @@ -1,6 +1,9 @@ -{ sources ? import ./sources.nix { }, pkgs ? import { } }: +{ nixpkgs ? , sources ? import ./sources.nix { } +, system ? builtins.currentSystem }: -let crate2nix = import sources.crate2nix { }; +let + pkgs = import ./nixpkgs.nix { inherit sources nixpkgs system; }; + crate2nix = import sources.crate2nix { inherit pkgs; }; in pkgs.mkShell { name = "veloren-shell"; nativeBuildInputs = with pkgs; [ @@ -11,7 +14,8 @@ in pkgs.mkShell { niv nixfmt crate2nix - rustup + cargo + rustc ]; buildInputs = with pkgs; [ alsaLib diff --git a/nix/sources.json b/nix/sources.json index 78a8c2782b..3f3f618c5f 100644 --- a/nix/sources.json +++ b/nix/sources.json @@ -5,10 +5,10 @@ "homepage": "", "owner": "kolloch", "repo": "crate2nix", - "rev": "87a544222723693c81f3b4141d639ba9b5769e2e", - "sha256": "0rml8xy7b6qlx631wpa29yb8qjsilv55w6g5rqbqjyw22aqz4ppb", + "rev": "5dfb0c155ca639591b82106b6455f24fc0a2f484", + "sha256": "0nkvpk3m8wgnwd94520jnsxjq14q255s67rl885i75rm293j2yha", "type": "tarball", - "url": "https://github.com/kolloch/crate2nix/archive/87a544222723693c81f3b4141d639ba9b5769e2e.tar.gz", + "url": "https://github.com/kolloch/crate2nix/archive/5dfb0c155ca639591b82106b6455f24fc0a2f484.tar.gz", "url_template": "https://github.com///archive/.tar.gz" }, "nixpkgsMoz": { @@ -17,10 +17,10 @@ "homepage": null, "owner": "mozilla", "repo": "nixpkgs-mozilla", - "rev": "e912ed483e980dfb4666ae0ed17845c4220e5e7c", - "sha256": "08fvzb8w80bkkabc1iyhzd15f4sm7ra10jn32kfch5klgl0gj3j3", + "rev": "18cd4300e9bf61c7b8b372f07af827f6ddc835bb", + "sha256": "1s0d1l5y7a3kygjbibssjnj7fcc87qaa5s9k4kda0j13j9h4zwgr", "type": "tarball", - "url": "https://github.com/mozilla/nixpkgs-mozilla/archive/e912ed483e980dfb4666ae0ed17845c4220e5e7c.tar.gz", + "url": "https://github.com/mozilla/nixpkgs-mozilla/archive/18cd4300e9bf61c7b8b372f07af827f6ddc835bb.tar.gz", "url_template": "https://github.com///archive/.tar.gz" } } diff --git a/nix/sources.nix b/nix/sources.nix index 0286dec14a..b20abf2307 100644 --- a/nix/sources.nix +++ b/nix/sources.nix @@ -1,5 +1,5 @@ # This file has been generated by Niv. - +{ system ? builtins.currentSystem }: let # @@ -61,7 +61,7 @@ let in if builtins.hasAttr "nixpkgs" sources then sourcesNixpkgs else if hasNixpkgsPath && !hasThisAsNixpkgsPath then - import { } + import { inherit system; } else abort '' Please specify either (through -I or NIX_PATH=nixpkgs=...) or From d5e9e1988179a893309ae2f542c4df6629f1b13b Mon Sep 17 00:00:00 2001 From: T-Dark0 Date: Thu, 16 Jul 2020 14:05:35 +0000 Subject: [PATCH 39/85] Created banned words file By default, it's an empty list --- server/src/alias_validator.rs | 115 ++++++++++++++++++++++++++++++++++ server/src/lib.rs | 26 ++++++++ server/src/settings.rs | 2 + server/src/sys/message.rs | 17 ++++- 4 files changed, 157 insertions(+), 3 deletions(-) create mode 100644 server/src/alias_validator.rs diff --git a/server/src/alias_validator.rs b/server/src/alias_validator.rs new file mode 100644 index 0000000000..d6e02f09c9 --- /dev/null +++ b/server/src/alias_validator.rs @@ -0,0 +1,115 @@ +use std::fmt::{self, Display}; + +#[derive(Debug, Default)] +pub struct AliasValidator { + banned_substrings: Vec, +} + +impl AliasValidator { + pub fn new(banned_substrings: Vec) -> Self { + let banned_substrings = banned_substrings + .iter() + .map(|string| string.to_lowercase()) + .collect(); + + AliasValidator { banned_substrings } + } + + pub fn validate(&self, alias: &str) -> Result<(), ValidatorError> { + let lowercase_alias = alias.to_lowercase(); + + for banned_word in self.banned_substrings.iter() { + if lowercase_alias.contains(banned_word) { + return Err(ValidatorError::Forbidden( + alias.to_owned(), + banned_word.to_owned(), + )); + } + } + Ok(()) + } +} + +#[derive(Debug, PartialEq)] +pub enum ValidatorError { + Forbidden(String, String), +} + +impl Display for ValidatorError { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Forbidden(name, _) => write!( + formatter, + "Character name \"{}\" contains a banned word", + name + ), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn multiple_matches() { + let banned_substrings = vec!["bad".to_owned(), "worse".to_owned()]; + let validator = AliasValidator::new(banned_substrings); + + let bad_alias = "Badplayery Mc WorsePlayeryFace"; + let result = validator.validate(bad_alias); + + assert_eq!( + result, + Err(ValidatorError::Forbidden( + bad_alias.to_owned(), + "bad".to_owned() + )) + ); + } + + #[test] + fn single_lowercase_match() { + let banned_substrings = vec!["blue".to_owned()]; + let validator = AliasValidator::new(banned_substrings); + + let bad_alias = "blueName"; + let result = validator.validate(bad_alias); + + assert_eq!( + result, + Err(ValidatorError::Forbidden( + bad_alias.to_owned(), + "blue".to_owned() + )) + ); + } + + #[test] + fn single_case_insensitive_match() { + let banned_substrings = vec!["GrEEn".to_owned()]; + let validator = AliasValidator::new(banned_substrings); + + let bad_alias = "gReenName"; + let result = validator.validate(bad_alias); + + assert_eq!( + result, + Err(ValidatorError::Forbidden( + bad_alias.to_owned(), + "green".to_owned() + )) + ); + } + + #[test] + fn mp_matches() { + let banned_substrings = vec!["orange".to_owned()]; + let validator = AliasValidator::new(banned_substrings); + + let good_alias = "ReasonableName"; + let result = validator.validate(good_alias); + + assert_eq!(result, Ok(())); + } +} diff --git a/server/src/lib.rs b/server/src/lib.rs index 4ed6b6272f..dadab8890b 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -2,6 +2,7 @@ #![allow(clippy::option_map_unit_fn)] #![feature(drain_filter, option_zip)] +pub mod alias_validator; pub mod auth_provider; pub mod chunk_generator; pub mod client; @@ -20,6 +21,7 @@ pub mod sys; pub use crate::{error::Error, events::Event, input::Input, settings::ServerSettings}; use crate::{ + alias_validator::AliasValidator, auth_provider::AuthProvider, chunk_generator::ChunkGenerator, client::{Client, RegionSubscription}, @@ -137,6 +139,30 @@ impl Server { state.ecs_mut().register::(); state.ecs_mut().register::(); + //Alias validator + let banned_words_paths = &settings.banned_words_files; + let mut banned_words = Vec::new(); + for path in banned_words_paths { + let mut list = match std::fs::File::open(&path) { + Ok(file) => match ron::de::from_reader(&file) { + Ok(vec) => vec, + Err(error) => { + tracing::warn!(?error, ?file, "Couldn't deserialize banned words file"); + return Err(Error::Other(error.to_string())); + }, + }, + Err(error) => { + tracing::warn!(?error, ?path, "couldn't open banned words file"); + return Err(Error::Other(error.to_string())); + }, + }; + banned_words.append(&mut list); + } + let banned_words_count = banned_words.len(); + tracing::debug!(?banned_words_count); + tracing::trace!(?banned_words); + state.ecs_mut().insert(AliasValidator::new(banned_words)); + #[cfg(feature = "worldgen")] let world = World::generate(settings.world_seed, WorldOpts { seed_elements: true, diff --git a/server/src/settings.rs b/server/src/settings.rs index 9e0d0f637a..8bc0c6f3a5 100644 --- a/server/src/settings.rs +++ b/server/src/settings.rs @@ -25,6 +25,7 @@ pub struct ServerSettings { pub map_file: Option, pub persistence_db_dir: String, pub max_view_distance: Option, + pub banned_words_files: Vec, } impl Default for ServerSettings { @@ -63,6 +64,7 @@ impl Default for ServerSettings { whitelist: Vec::new(), persistence_db_dir: "saves".to_owned(), max_view_distance: Some(30), + banned_words_files: Vec::new(), } } } diff --git a/server/src/sys/message.rs b/server/src/sys/message.rs index f9a4918723..8e8f604810 100644 --- a/server/src/sys/message.rs +++ b/server/src/sys/message.rs @@ -1,7 +1,7 @@ use super::SysTimer; use crate::{ - auth_provider::AuthProvider, client::Client, persistence::character::CharacterLoader, - ServerSettings, CLIENT_TIMEOUT, + alias_validator::AliasValidator, auth_provider::AuthProvider, client::Client, + persistence::character::CharacterLoader, ServerSettings, CLIENT_TIMEOUT, }; use common::{ comp::{ @@ -55,6 +55,7 @@ impl Sys { players: &mut WriteStorage<'_, Player>, controllers: &mut WriteStorage<'_, Controller>, settings: &Read<'_, ServerSettings>, + alias_validator: &ReadExpect<'_, AliasValidator>, ) -> Result<(), crate::error::Error> { loop { let msg = client.recv().await?; @@ -347,7 +348,14 @@ impl Sys { } }, ClientMsg::CreateCharacter { alias, tool, body } => { - if let Some(player) = players.get(entity) { + if let Err(error) = alias_validator.validate(&alias) { + tracing::debug!( + ?error, + ?alias, + "denied alias as it contained a banned word" + ); + client.notify(ServerMsg::CharacterActionError(error.to_string())); + } else if let Some(player) = players.get(entity) { character_loader.create_character( entity, player.uuid().to_string(), @@ -413,6 +421,7 @@ impl<'a> System<'a> for Sys { WriteStorage<'a, Client>, WriteStorage<'a, Controller>, Read<'a, ServerSettings>, + ReadExpect<'a, AliasValidator>, ); #[allow(clippy::match_ref_pats)] // TODO: Pending review in #587 @@ -443,6 +452,7 @@ impl<'a> System<'a> for Sys { mut clients, mut controllers, settings, + alias_validator, ): Self::SystemData, ) { timer.start(); @@ -502,6 +512,7 @@ impl<'a> System<'a> for Sys { &mut players, &mut controllers, &settings, + &alias_validator, ).fuse() => err, ) }); From 30bd570b8f842c64667a6b5d0bf653f90fdafe73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=A4rtens?= Date: Fri, 17 Jul 2020 12:17:34 +0200 Subject: [PATCH 40/85] put optional builds in check like imbris suggested in order to test macOS and other builds easily --- .gitlab/CI/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab/CI/release.yml b/.gitlab/CI/release.yml index 5f0dfc86ea..7a59bd5b60 100644 --- a/.gitlab/CI/release.yml +++ b/.gitlab/CI/release.yml @@ -1,6 +1,6 @@ # allow_failure: true makes these pipelines manual and "non-blocking" which changed with except -> rule syntax .optional-release: - stage: build + stage: check tags: - veloren-docker rules: From 9e880009e098ebd3ef326fc2aedd85dda849daa9 Mon Sep 17 00:00:00 2001 From: Imbris Date: Fri, 17 Jul 2020 01:01:21 -0400 Subject: [PATCH 41/85] Loosen in_vd calculation on the server so that it accepts all chunks a client with the specified vd would request --- server/src/sys/message.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/server/src/sys/message.rs b/server/src/sys/message.rs index 8e8f604810..625b57c7d8 100644 --- a/server/src/sys/message.rs +++ b/server/src/sys/message.rs @@ -317,7 +317,8 @@ impl Sys { pos.0.xy().map(|e| e as f64).distance( key.map(|e| e as f64 + 0.5) * TerrainChunkSize::RECT_SIZE.map(|e| e as f64), - ) < (view_distance as f64 + 1.5) * TerrainChunkSize::RECT_SIZE.x as f64 + ) < (view_distance as f64 - 1.0 + 2.5 * 2.0_f64.sqrt()) + * TerrainChunkSize::RECT_SIZE.x as f64 } else { true }; From 6cce09be851aca201d2c540dcd0233741e5a75ec Mon Sep 17 00:00:00 2001 From: Samuel Keiffer Date: Sat, 18 Jul 2020 00:05:28 +0000 Subject: [PATCH 42/85] Added protection stat to admin armor hidden in debug folder. Removed blue cultist armor set from drop table as it is an admin set. --- CHANGELOG.md | 1 + Cargo.lock | 50 +++++ assets/common/items/armor/back/admin.ron | 10 +- assets/common/items/armor/back/short_0.ron | 10 +- assets/common/items/armor/belt/assassin.ron | 10 +- .../common/items/armor/belt/cloth_blue_0.ron | 10 +- .../common/items/armor/belt/cloth_green_0.ron | 10 +- .../items/armor/belt/cloth_purple_0.ron | 10 +- .../common/items/armor/belt/cultist_belt.ron | 10 +- assets/common/items/armor/belt/druid.ron | 10 +- assets/common/items/armor/belt/leather_0.ron | 10 +- assets/common/items/armor/belt/leather_2.ron | 8 +- assets/common/items/armor/belt/plate_0.ron | 10 +- assets/common/items/armor/belt/steel_0.ron | 10 +- assets/common/items/armor/belt/twig.ron | 10 +- .../common/items/armor/belt/twigsflowers.ron | 10 +- .../common/items/armor/belt/twigsleaves.ron | 10 +- assets/common/items/armor/chest/assassin.ron | 10 +- .../common/items/armor/chest/cloth_blue_0.ron | 10 +- .../items/armor/chest/cloth_green_0.ron | 10 +- .../items/armor/chest/cloth_purple_0.ron | 10 +- .../items/armor/chest/cultist_chest_blue.ron | 10 +- .../armor/chest/cultist_chest_purple.ron | 10 +- assets/common/items/armor/chest/druid.ron | 10 +- assets/common/items/armor/chest/leather_0.ron | 10 +- assets/common/items/armor/chest/leather_2.ron | 10 +- .../items/armor/chest/plate_green_0.ron | 10 +- assets/common/items/armor/chest/steel_0.ron | 10 +- assets/common/items/armor/chest/twig.ron | 10 +- .../common/items/armor/chest/twigsflowers.ron | 10 +- .../common/items/armor/chest/twigsleaves.ron | 10 +- .../items/armor/chest/worker_green_0.ron | 10 +- .../items/armor/chest/worker_green_1.ron | 10 +- .../items/armor/chest/worker_orange_0.ron | 10 +- .../items/armor/chest/worker_orange_1.ron | 10 +- .../items/armor/chest/worker_purple_0.ron | 10 +- .../items/armor/chest/worker_purple_1.ron | 10 +- .../common/items/armor/chest/worker_red_0.ron | 10 +- .../common/items/armor/chest/worker_red_1.ron | 10 +- .../items/armor/chest/worker_yellow_0.ron | 10 +- .../items/armor/chest/worker_yellow_1.ron | 10 +- assets/common/items/armor/foot/assassin.ron | 10 +- .../common/items/armor/foot/cloth_blue_0.ron | 10 +- .../common/items/armor/foot/cloth_green_0.ron | 10 +- .../items/armor/foot/cloth_purple_0.ron | 10 +- .../common/items/armor/foot/cultist_boots.ron | 10 +- assets/common/items/armor/foot/druid.ron | 10 +- .../items/armor/foot/jackalope_slippers.ron | 10 +- assets/common/items/armor/foot/leather_0.ron | 10 +- assets/common/items/armor/foot/leather_2.ron | 10 +- assets/common/items/armor/foot/plate_0.ron | 10 +- assets/common/items/armor/foot/steel_0.ron | 10 +- assets/common/items/armor/foot/twig.ron | 10 +- .../common/items/armor/foot/twigsflowers.ron | 10 +- .../common/items/armor/foot/twigsleaves.ron | 10 +- assets/common/items/armor/hand/assassin.ron | 10 +- .../common/items/armor/hand/cloth_blue_0.ron | 10 +- .../common/items/armor/hand/cloth_green_0.ron | 10 +- .../items/armor/hand/cloth_purple_0.ron | 10 +- .../items/armor/hand/cultist_hands_blue.ron | 10 +- .../items/armor/hand/cultist_hands_purple.ron | 10 +- assets/common/items/armor/hand/druid.ron | 10 +- assets/common/items/armor/hand/leather_0.ron | 10 +- assets/common/items/armor/hand/leather_2.ron | 10 +- assets/common/items/armor/hand/plate_0.ron | 10 +- assets/common/items/armor/hand/steel_0.ron | 10 +- assets/common/items/armor/hand/twig.ron | 10 +- .../common/items/armor/hand/twigsflowers.ron | 10 +- .../common/items/armor/hand/twigsleaves.ron | 10 +- .../common/items/armor/head/assa_mask_0.ron | 10 +- assets/common/items/armor/head/leather_0.ron | 10 +- assets/common/items/armor/neck/neck_0.ron | 10 +- assets/common/items/armor/pants/assassin.ron | 10 +- .../common/items/armor/pants/cloth_blue_0.ron | 10 +- .../items/armor/pants/cloth_green_0.ron | 10 +- .../items/armor/pants/cloth_purple_0.ron | 10 +- .../items/armor/pants/cultist_legs_blue.ron | 10 +- .../items/armor/pants/cultist_legs_purple.ron | 10 +- assets/common/items/armor/pants/druid.ron | 10 +- assets/common/items/armor/pants/hunting.ron | 10 +- assets/common/items/armor/pants/leather_0.ron | 10 +- assets/common/items/armor/pants/leather_2.ron | 10 +- .../items/armor/pants/plate_green_0.ron | 10 +- assets/common/items/armor/pants/steel_0.ron | 10 +- assets/common/items/armor/pants/twig.ron | 10 +- .../common/items/armor/pants/twigsflowers.ron | 10 +- .../common/items/armor/pants/twigsleaves.ron | 10 +- .../items/armor/pants/worker_blue_0.ron | 10 +- assets/common/items/armor/ring/ring_0.ron | 10 +- .../common/items/armor/shoulder/assassin.ron | 10 +- .../items/armor/shoulder/cloth_blue_0.ron | 10 +- .../items/armor/shoulder/cloth_blue_1.ron | 10 +- .../items/armor/shoulder/cloth_green_0.ron | 10 +- .../items/armor/shoulder/cloth_purple_0.ron | 10 +- .../armor/shoulder/cultist_shoulder_blue.ron | 10 +- .../shoulder/cultist_shoulder_purple.ron | 10 +- .../items/armor/shoulder/druidshoulder.ron | 10 +- .../items/armor/shoulder/iron_spikes.ron | 10 +- .../common/items/armor/shoulder/leather_0.ron | 10 +- .../common/items/armor/shoulder/leather_1.ron | 10 +- .../common/items/armor/shoulder/leather_2.ron | 10 +- .../items/armor/shoulder/leather_iron_0.ron | 10 +- .../items/armor/shoulder/leather_iron_1.ron | 10 +- .../items/armor/shoulder/leather_iron_2.ron | 10 +- .../items/armor/shoulder/leather_iron_3.ron | 10 +- .../items/armor/shoulder/leather_strips.ron | 10 +- .../common/items/armor/shoulder/plate_0.ron | 10 +- .../common/items/armor/shoulder/steel_0.ron | 10 +- assets/common/items/armor/shoulder/twigs.ron | 10 +- .../items/armor/shoulder/twigsflowers.ron | 10 +- .../items/armor/shoulder/twigsleaves.ron | 10 +- .../items/armor/starter/rugged_chest.ron | 10 +- .../items/armor/starter/rugged_pants.ron | 10 +- .../common/items/armor/starter/sandals_0.ron | 10 +- assets/common/items/armor/tabard/admin.ron | 10 +- assets/common/items/debug/admin.ron | 10 +- assets/common/items/debug/admin_back.ron | 10 +- assets/common/items/debug/cultist_belt.ron | 10 +- assets/common/items/debug/cultist_boots.ron | 10 +- .../common/items/debug/cultist_chest_blue.ron | 10 +- .../common/items/debug/cultist_hands_blue.ron | 10 +- .../common/items/debug/cultist_legs_blue.ron | 10 +- .../items/debug/cultist_shoulder_blue.ron | 10 +- assets/common/items/testing/test_boots.ron | 8 +- assets/common/loot_table.ron | 141 ++++++++++---- assets/voxygen/element/icons/protection.png | 3 + assets/voxygen/i18n/en.ron | 2 + common/Cargo.toml | 1 + common/src/comp/ability.rs | 64 +++++-- common/src/comp/body.rs | 108 +++++++++++ common/src/comp/inventory/item/armor.rs | 26 ++- common/src/comp/inventory/item/mod.rs | 15 +- common/src/comp/inventory/mod.rs | 4 +- common/src/comp/inventory/slot.rs | 61 +++--- common/src/comp/stats.rs | 74 -------- common/src/loadout_builder.rs | 38 ---- common/src/sys/combat.rs | 12 +- .../down.sql | 1 + .../up.sql | 29 +++ voxygen/src/hud/bag.rs | 179 +++++++----------- voxygen/src/hud/img_ids.rs | 1 + voxygen/src/hud/item_imgs.rs | 6 +- voxygen/src/hud/mod.rs | 1 + voxygen/src/hud/util.rs | 85 +++++++++ voxygen/src/scene/figure/cache.rs | 72 +++++-- voxygen/src/scene/figure/load.rs | 58 +++--- 146 files changed, 1515 insertions(+), 733 deletions(-) create mode 100644 assets/voxygen/element/icons/protection.png create mode 100644 server/src/migrations/2020-07-16-044718_migrate_armour_stats/down.sql create mode 100644 server/src/migrations/2020-07-16-044718_migrate_armour_stats/up.sql create mode 100644 voxygen/src/hud/util.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 94cc10600e..2ae20e10bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,6 +44,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Creature specific stats - Minimap compass - Initial crafting system implementation +- Protection stat to armor that reduces incoming damage ### Changed diff --git a/Cargo.lock b/Cargo.lock index 3fd74c3e2b..a847fa1d48 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -108,6 +108,18 @@ dependencies = [ "syn 1.0.33", ] +[[package]] +name = "arraygen" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc755b23c19211c270ef000fa7ce871377825e6cc7d1bfd0311076f22c5e6ba1" +dependencies = [ + "proc-macro-error", + "proc-macro2 1.0.18", + "quote 1.0.7", + "syn 1.0.33", +] + [[package]] name = "arrayref" version = "0.3.6" @@ -2945,6 +2957,32 @@ version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea" +[[package]] +name = "proc-macro-error" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18f33027081eba0a6d8aba6d1b1c3a3be58cbb12106341c2d5759fcd9b5277e7" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2 1.0.18", + "quote 1.0.7", + "syn 1.0.33", + "version_check 0.9.2", +] + +[[package]] +name = "proc-macro-error-attr" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a5b4b77fdb63c1eca72173d68d24501c54ab1269409f6b672c85deb18af69de" +dependencies = [ + "proc-macro2 1.0.18", + "quote 1.0.7", + "syn 1.0.33", + "syn-mid", + "version_check 0.9.2", +] + [[package]] name = "proc-macro-hack" version = "0.5.16" @@ -3827,6 +3865,17 @@ dependencies = [ "unicode-xid 0.2.0", ] +[[package]] +name = "syn-mid" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7be3539f6c128a931cf19dcee741c1af532c7fd387baa739c03dd2e96479338a" +dependencies = [ + "proc-macro2 1.0.18", + "quote 1.0.7", + "syn 1.0.33", +] + [[package]] name = "synstructure" version = "0.12.4" @@ -4373,6 +4422,7 @@ dependencies = [ name = "veloren-common" version = "0.6.0" dependencies = [ + "arraygen", "authc", "criterion", "crossbeam", diff --git a/assets/common/items/armor/back/admin.ron b/assets/common/items/armor/back/admin.ron index f07273b7c6..1e7e0a313b 100644 --- a/assets/common/items/armor/back/admin.ron +++ b/assets/common/items/armor/back/admin.ron @@ -1,8 +1,12 @@ Item( name: "Admin's Cape", - description: "Back\n\nArmor: 0\n\nWith great power comes\ngreat responsibility.\n\n", + description: "With great power comes\ngreat responsibility.", kind: Armor( - kind: Back(Admin), - stats: (20), + ( + kind: Back(Admin), + stats: ( + protection: Normal(0.0), + ), + ) ), ) diff --git a/assets/common/items/armor/back/short_0.ron b/assets/common/items/armor/back/short_0.ron index f1759c553d..a1093e90b8 100644 --- a/assets/common/items/armor/back/short_0.ron +++ b/assets/common/items/armor/back/short_0.ron @@ -1,8 +1,12 @@ Item( name: "Short leather Cape", - description: "Back\n\nArmor: 0\n\n", + description: "Probably made of the finest leather.", kind: Armor( - kind: Back(Short0), - stats: (20), + ( + kind: Back(Short0), + stats: ( + protection: Normal(0.0), + ), + ) ), ) diff --git a/assets/common/items/armor/belt/assassin.ron b/assets/common/items/armor/belt/assassin.ron index dc086baa18..2c00d9066a 100644 --- a/assets/common/items/armor/belt/assassin.ron +++ b/assets/common/items/armor/belt/assassin.ron @@ -1,8 +1,12 @@ Item( name: "Assassin Belt", - description: "Belt\n\nArmor: 0\n\nOnly the best for a member of the creed.\n\n", + description: "Only the best for a member of the creed.", kind: Armor( - kind: Belt(Assassin), - stats: (20), + ( + kind: Belt(Assassin), + stats: ( + protection: Normal(0.0), + ), + ) ), ) diff --git a/assets/common/items/armor/belt/cloth_blue_0.ron b/assets/common/items/armor/belt/cloth_blue_0.ron index 1faaafb7f3..ef9085cf85 100644 --- a/assets/common/items/armor/belt/cloth_blue_0.ron +++ b/assets/common/items/armor/belt/cloth_blue_0.ron @@ -1,8 +1,12 @@ Item( name: "Blue Linen Belt", - description: "Belt\n\nArmor: 0\n\nSoft and warm\n\n", + description: "Soft and warm", kind: Armor( - kind: Belt(ClothBlue0), - stats: (20), + ( + kind: Belt(ClothBlue0), + stats: ( + protection: Normal(0.0), + ), + ) ), ) diff --git a/assets/common/items/armor/belt/cloth_green_0.ron b/assets/common/items/armor/belt/cloth_green_0.ron index 15be2149ea..b4cba9679d 100644 --- a/assets/common/items/armor/belt/cloth_green_0.ron +++ b/assets/common/items/armor/belt/cloth_green_0.ron @@ -1,8 +1,12 @@ Item( name: "Green Linen Belt", - description: "Belt\n\nArmor: 0\n\nSoft and warm\n\n", + description: "Soft and warm.", kind: Armor( - kind: Belt(ClothGreen0), - stats: (20), + ( + kind: Belt(ClothGreen0), + stats: ( + protection: Normal(0.0), + ), + ) ), ) diff --git a/assets/common/items/armor/belt/cloth_purple_0.ron b/assets/common/items/armor/belt/cloth_purple_0.ron index 7060fd9c83..54cc8647dc 100644 --- a/assets/common/items/armor/belt/cloth_purple_0.ron +++ b/assets/common/items/armor/belt/cloth_purple_0.ron @@ -1,8 +1,12 @@ Item( name: "Purple Linen Belt", - description: "Belt\n\nArmor: 0\n\nSoft and warm\n\n", + description: "Soft and warm.", kind: Armor( - kind: Belt(ClothPurple0), - stats: (20), + ( + kind: Belt(ClothPurple0), + stats: ( + protection: Normal(0.0), + ), + ) ), ) diff --git a/assets/common/items/armor/belt/cultist_belt.ron b/assets/common/items/armor/belt/cultist_belt.ron index 1b2765d2c8..439851225a 100644 --- a/assets/common/items/armor/belt/cultist_belt.ron +++ b/assets/common/items/armor/belt/cultist_belt.ron @@ -1,8 +1,12 @@ Item( name: "Cultist Belt", - description: "Belt\n\nArmor: 0\n\n", + description: "Ceremonial attire used by members.", kind: Armor( - kind: Belt(Cultist), - stats: (20), + ( + kind: Belt(Cultist), + stats: ( + protection: Normal(0.0), + ), + ) ), ) diff --git a/assets/common/items/armor/belt/druid.ron b/assets/common/items/armor/belt/druid.ron index 0a761f49c0..941ebbe6ca 100644 --- a/assets/common/items/armor/belt/druid.ron +++ b/assets/common/items/armor/belt/druid.ron @@ -1,8 +1,12 @@ Item( name: "Druid's Belt", - description: "Twisted vines to keep everything secure.\n\n", + description: "Twisted vines to keep everything secure.", kind: Armor( - kind: Belt(Druid), - stats: (20), + ( + kind: Belt(Druid), + stats: ( + protection: Normal(0.0), + ), + ) ), ) diff --git a/assets/common/items/armor/belt/leather_0.ron b/assets/common/items/armor/belt/leather_0.ron index 6ca49af665..6c7b499bfb 100644 --- a/assets/common/items/armor/belt/leather_0.ron +++ b/assets/common/items/armor/belt/leather_0.ron @@ -1,8 +1,12 @@ Item( name: "Swift Belt", - description: "Belt\n\nArmor: 0\n\nSwift like the wind.\n\n", + description: "Swift like the wind.", kind: Armor( - kind: Belt(Leather0), - stats: (20), + ( + kind: Belt(Leather0), + stats: ( + protection: Normal(0.0), + ), + ) ), ) diff --git a/assets/common/items/armor/belt/leather_2.ron b/assets/common/items/armor/belt/leather_2.ron index ae3fd773f4..fb2d1d05a4 100644 --- a/assets/common/items/armor/belt/leather_2.ron +++ b/assets/common/items/armor/belt/leather_2.ron @@ -2,7 +2,11 @@ Item( name: "Leather Belt", description: "A belt made from simple leather.", kind: Armor( - kind: Belt(Leather2), - stats: (20), + ( + kind: Belt(Leather2), + stats: ( + protection: Normal(0.0), + ), + ) ), ) diff --git a/assets/common/items/armor/belt/plate_0.ron b/assets/common/items/armor/belt/plate_0.ron index f1269346f1..c48c0bdb0a 100644 --- a/assets/common/items/armor/belt/plate_0.ron +++ b/assets/common/items/armor/belt/plate_0.ron @@ -1,8 +1,12 @@ Item( name: "Iron Belt", - description: "Belt\n\nArmor: 0\n\n", + description: "A belt with a buckle forged from iron.", kind: Armor( - kind: Belt(Plate0), - stats: (20), + ( + kind: Belt(Plate0), + stats: ( + protection: Normal(0.0), + ), + ) ), ) diff --git a/assets/common/items/armor/belt/steel_0.ron b/assets/common/items/armor/belt/steel_0.ron index 03086db2f9..0b9d7df412 100644 --- a/assets/common/items/armor/belt/steel_0.ron +++ b/assets/common/items/armor/belt/steel_0.ron @@ -1,8 +1,12 @@ Item( name: "Steel Belt", - description: "A belt made from Steel.", + description: "A belt forged from steel.", kind: Armor( - kind: Belt(Steel0), - stats: (20), + ( + kind: Belt(Steel0), + stats: ( + protection: Normal(0.0), + ), + ) ), ) diff --git a/assets/common/items/armor/belt/twig.ron b/assets/common/items/armor/belt/twig.ron index 42c53c5dbb..754206366b 100644 --- a/assets/common/items/armor/belt/twig.ron +++ b/assets/common/items/armor/belt/twig.ron @@ -1,8 +1,12 @@ Item( name: "Twig Belt", - description: "A belt made from woven from twigs.\n\n", + description: "A belt made from woven from twigs.", kind: Armor( - kind: Belt(Twig), - stats: (20), + ( + kind: Belt(Twig), + stats: ( + protection: Normal(0.0), + ), + ) ), ) diff --git a/assets/common/items/armor/belt/twigsflowers.ron b/assets/common/items/armor/belt/twigsflowers.ron index f11985c104..0d503ef7d7 100644 --- a/assets/common/items/armor/belt/twigsflowers.ron +++ b/assets/common/items/armor/belt/twigsflowers.ron @@ -1,8 +1,12 @@ Item( name: "Flowery Belt", - description: "A belt woven from twigs and flowers.\n\n", + description: "A belt woven from twigs and flowers.", kind: Armor( - kind: Belt(Twigsflowers), - stats: (20), + ( + kind: Belt(Twigsflowers), + stats: ( + protection: Normal(0.0), + ), + ) ), ) diff --git a/assets/common/items/armor/belt/twigsleaves.ron b/assets/common/items/armor/belt/twigsleaves.ron index 5f00fce3e8..883f1ebe77 100644 --- a/assets/common/items/armor/belt/twigsleaves.ron +++ b/assets/common/items/armor/belt/twigsleaves.ron @@ -1,8 +1,12 @@ Item( name: "Leafy Belt", - description: "A belt woven from twigs and leaves.\n\n", + description: "A belt woven from twigs and leaves.", kind: Armor( - kind: Belt(Twigsleaves), - stats: (20), + ( + kind: Belt(Twigsleaves), + stats: ( + protection: Normal(0.0), + ), + ) ), ) diff --git a/assets/common/items/armor/chest/assassin.ron b/assets/common/items/armor/chest/assassin.ron index ba969ddd30..cbe034b1f1 100644 --- a/assets/common/items/armor/chest/assassin.ron +++ b/assets/common/items/armor/chest/assassin.ron @@ -1,8 +1,12 @@ Item( name: "Assassin Chest", - description: "Chest\n\nArmor: 0\n\nOnly the best for a member of the creed.\n\n", + description: "Only the best for a member of the creed.", kind: Armor( - kind: Chest(Assassin), - stats: (20), + ( + kind: Chest(Assassin), + stats: ( + protection: Normal(6.0), + ), + ) ), ) diff --git a/assets/common/items/armor/chest/cloth_blue_0.ron b/assets/common/items/armor/chest/cloth_blue_0.ron index a36d093faf..d2a69a427c 100644 --- a/assets/common/items/armor/chest/cloth_blue_0.ron +++ b/assets/common/items/armor/chest/cloth_blue_0.ron @@ -1,8 +1,12 @@ Item( name: "Blue Linen Chest", - description: "Chest\n\nArmor: 0\n\nSoft and warm\n\n", + description: "Soft and warm.", kind: Armor( - kind: Chest(ClothBlue0), - stats: (20), + ( + kind: Chest(ClothBlue0), + stats: ( + protection: Normal(1.0), + ), + ) ), ) diff --git a/assets/common/items/armor/chest/cloth_green_0.ron b/assets/common/items/armor/chest/cloth_green_0.ron index 0098c1b869..840c3f1c37 100644 --- a/assets/common/items/armor/chest/cloth_green_0.ron +++ b/assets/common/items/armor/chest/cloth_green_0.ron @@ -1,8 +1,12 @@ Item( name: "Green Linen Chest", - description: "Chest\n\nArmor: 0\n\nSoft and warm\n\n", + description: "Soft and warm.", kind: Armor( - kind: Chest(ClothGreen0), - stats: (20), + ( + kind: Chest(ClothGreen0), + stats: ( + protection: Normal(1.0), + ), + ) ), ) diff --git a/assets/common/items/armor/chest/cloth_purple_0.ron b/assets/common/items/armor/chest/cloth_purple_0.ron index fef6404226..b77ab71883 100644 --- a/assets/common/items/armor/chest/cloth_purple_0.ron +++ b/assets/common/items/armor/chest/cloth_purple_0.ron @@ -1,8 +1,12 @@ Item( name: "Purple Linen Chest", - description: "Chest\n\nArmor: 0\n\nSoft and warm\n\n", + description: "Soft and warm.", kind: Armor( - kind: Chest(ClothPurple0), - stats: (20), + ( + kind: Chest(ClothPurple0), + stats: ( + protection: Normal(1.0), + ), + ) ), ) diff --git a/assets/common/items/armor/chest/cultist_chest_blue.ron b/assets/common/items/armor/chest/cultist_chest_blue.ron index 2039ea6d76..ddb8dcac4c 100644 --- a/assets/common/items/armor/chest/cultist_chest_blue.ron +++ b/assets/common/items/armor/chest/cultist_chest_blue.ron @@ -1,8 +1,12 @@ Item( name: "Blue Cultist Chest", - description: "Chest\n\nArmor: 0\n\n", + description: "Ceremonial attire used by members.", kind: Armor( - kind: Chest(CultistBlue), - stats: (20), + ( + kind: Chest(CultistBlue), + stats: ( + protection: Normal(5.0), + ), + ) ), ) diff --git a/assets/common/items/armor/chest/cultist_chest_purple.ron b/assets/common/items/armor/chest/cultist_chest_purple.ron index a3bf40b1ff..d043e49512 100644 --- a/assets/common/items/armor/chest/cultist_chest_purple.ron +++ b/assets/common/items/armor/chest/cultist_chest_purple.ron @@ -1,8 +1,12 @@ Item( name: "Purple Cultist Chest", - description: "Chest\n\nArmor: 0\n\n", + description: "Ceremonial attire used by members.", kind: Armor( - kind: Chest(CultistPurple), - stats: (20), + ( + kind: Chest(CultistPurple), + stats: ( + protection: Normal(5.0), + ), + ) ), ) diff --git a/assets/common/items/armor/chest/druid.ron b/assets/common/items/armor/chest/druid.ron index 71ad9e1bef..96855c249a 100644 --- a/assets/common/items/armor/chest/druid.ron +++ b/assets/common/items/armor/chest/druid.ron @@ -1,8 +1,12 @@ Item( name: "Druid's Vest", - description: "Druidic chest wrappings.\n\n", + description: "Druidic chest wrappings.", kind: Armor( - kind: Chest(Druid), - stats: (20), + ( + kind: Chest(Druid), + stats: ( + protection: Normal(6.0), + ), + ) ), ) diff --git a/assets/common/items/armor/chest/leather_0.ron b/assets/common/items/armor/chest/leather_0.ron index 1135163213..21767b4c06 100644 --- a/assets/common/items/armor/chest/leather_0.ron +++ b/assets/common/items/armor/chest/leather_0.ron @@ -1,8 +1,12 @@ Item( name: "Swift Chest", - description: "Chest\n\nArmor: 0\n\nSwift like the wind.\n\n", + description: "Swift like the wind.", kind: Armor( - kind: Chest(Leather0), - stats: (20), + ( + kind: Chest(Leather0), + stats: ( + protection: Normal(10.0), + ), + ) ), ) diff --git a/assets/common/items/armor/chest/leather_2.ron b/assets/common/items/armor/chest/leather_2.ron index 1a046a7942..75661e56a0 100644 --- a/assets/common/items/armor/chest/leather_2.ron +++ b/assets/common/items/armor/chest/leather_2.ron @@ -1,8 +1,12 @@ Item( name: "Leather Cuirass", - description: "A cuirass made of simple leather", + description: "A cuirass made of simple leather.", kind: Armor( - kind: Chest(Leather2), - stats: (20), + ( + kind: Chest(Leather2), + stats: ( + protection: Normal(10.0), + ), + ) ), ) \ No newline at end of file diff --git a/assets/common/items/armor/chest/plate_green_0.ron b/assets/common/items/armor/chest/plate_green_0.ron index aabc7ab943..dda388739b 100644 --- a/assets/common/items/armor/chest/plate_green_0.ron +++ b/assets/common/items/armor/chest/plate_green_0.ron @@ -1,8 +1,12 @@ Item( name: "Iron Chestplate", - description: "Chest\n\nArmor: 0\n\n", + description: "A chestplate forged from iron.", kind: Armor( - kind: Chest(PlateGreen0), - stats: (20), + ( + kind: Chest(PlateGreen0), + stats: ( + protection: Normal(20.0), + ), + ) ), ) diff --git a/assets/common/items/armor/chest/steel_0.ron b/assets/common/items/armor/chest/steel_0.ron index c97a49a7c4..314bf57e95 100644 --- a/assets/common/items/armor/chest/steel_0.ron +++ b/assets/common/items/armor/chest/steel_0.ron @@ -1,8 +1,12 @@ Item( name: "Steel Cuirass", - description: "A cuirass of steel plate", + description: "A cuirass of steel plate.", kind: Armor( - kind: Chest(Steel0), - stats: (20), + ( + kind: Chest(Steel0), + stats: ( + protection: Normal(25.0), + ), + ) ), ) \ No newline at end of file diff --git a/assets/common/items/armor/chest/twig.ron b/assets/common/items/armor/chest/twig.ron index 6dd728f2bf..56c3584569 100644 --- a/assets/common/items/armor/chest/twig.ron +++ b/assets/common/items/armor/chest/twig.ron @@ -1,8 +1,12 @@ Item( name: "Twig Shirt", - description: "A shirt woven from twigs.\n\n", + description: "A shirt woven from twigs.", kind: Armor( - kind: Chest(Twig), - stats: (20), + ( + kind: Chest(Twig), + stats: ( + protection: Normal(15.0), + ), + ) ), ) diff --git a/assets/common/items/armor/chest/twigsflowers.ron b/assets/common/items/armor/chest/twigsflowers.ron index 698ae8dc64..8b364b55e9 100644 --- a/assets/common/items/armor/chest/twigsflowers.ron +++ b/assets/common/items/armor/chest/twigsflowers.ron @@ -1,8 +1,12 @@ Item( name: "Flowery Shirt", - description: "A shirt woven from twigs and flowers.\n\n", + description: "A shirt woven from twigs and flowers.", kind: Armor( - kind: Chest(Twigsflowers), - stats: (20), + ( + kind: Chest(Twigsflowers), + stats: ( + protection: Normal(15.0), + ), + ) ), ) diff --git a/assets/common/items/armor/chest/twigsleaves.ron b/assets/common/items/armor/chest/twigsleaves.ron index e1cc509b9e..336182d654 100644 --- a/assets/common/items/armor/chest/twigsleaves.ron +++ b/assets/common/items/armor/chest/twigsleaves.ron @@ -1,8 +1,12 @@ Item( name: "Leafy Shirt", - description: "A shirt woven from twigs and leaves.\n\n", + description: "A shirt woven from twigs and leaves.", kind: Armor( - kind: Chest(Twigsleaves), - stats: (20), + ( + kind: Chest(Twigsleaves), + stats: ( + protection: Normal(15.0), + ), + ) ), ) diff --git a/assets/common/items/armor/chest/worker_green_0.ron b/assets/common/items/armor/chest/worker_green_0.ron index d474d10ddf..90f884c50b 100644 --- a/assets/common/items/armor/chest/worker_green_0.ron +++ b/assets/common/items/armor/chest/worker_green_0.ron @@ -1,8 +1,12 @@ Item( name: "Green Worker Shirt", - description: "Chest\n\nArmor: 0\n\n", + description: "Was used by a farmer, until recently.", kind: Armor( - kind: Chest(WorkerGreen0), - stats: (20), + ( + kind: Chest(WorkerGreen0), + stats: ( + protection: Normal(1.0), + ), + ) ), ) diff --git a/assets/common/items/armor/chest/worker_green_1.ron b/assets/common/items/armor/chest/worker_green_1.ron index bae09a6fe9..5a52626017 100644 --- a/assets/common/items/armor/chest/worker_green_1.ron +++ b/assets/common/items/armor/chest/worker_green_1.ron @@ -1,8 +1,12 @@ Item( name: "Green Worker Shirt", - description: "Chest\n\nArmor: 0\n\n", + description: "Was used by a farmer, until recently.", kind: Armor( - kind: Chest(WorkerGreen1), - stats: (20), + ( + kind: Chest(WorkerGreen1), + stats: ( + protection: Normal(1.0), + ), + ) ), ) diff --git a/assets/common/items/armor/chest/worker_orange_0.ron b/assets/common/items/armor/chest/worker_orange_0.ron index 6de8ffca79..a65db86c4e 100644 --- a/assets/common/items/armor/chest/worker_orange_0.ron +++ b/assets/common/items/armor/chest/worker_orange_0.ron @@ -1,8 +1,12 @@ Item( name: "Orange Worker Shirt", - description: "Chest\n\nArmor: 0\n\n", + description: "Was used by a farmer, until recently.", kind: Armor( - kind: Chest(WorkerOrange0), - stats: (20), + ( + kind: Chest(WorkerOrange0), + stats: ( + protection: Normal(1.0), + ), + ) ), ) diff --git a/assets/common/items/armor/chest/worker_orange_1.ron b/assets/common/items/armor/chest/worker_orange_1.ron index 02e1f57b77..b0e41761c1 100644 --- a/assets/common/items/armor/chest/worker_orange_1.ron +++ b/assets/common/items/armor/chest/worker_orange_1.ron @@ -1,8 +1,12 @@ Item( name: "Orange Worker Shirt", - description: "Chest\n\nArmor: 0\n\n", + description: "Was used by a farmer, until recently.", kind: Armor( - kind: Chest(WorkerOrange1), - stats: (20), + ( + kind: Chest(WorkerOrange1), + stats: ( + protection: Normal(1.0), + ), + ) ), ) diff --git a/assets/common/items/armor/chest/worker_purple_0.ron b/assets/common/items/armor/chest/worker_purple_0.ron index f9fc9b26a5..b8dd21d140 100644 --- a/assets/common/items/armor/chest/worker_purple_0.ron +++ b/assets/common/items/armor/chest/worker_purple_0.ron @@ -1,8 +1,12 @@ Item( name: "Purple Worker Shirt", - description: "Chest\n\nArmor: 0\n\n", + description: "Was used by a farmer, until recently.", kind: Armor( - kind: Chest(WorkerPurple0), - stats: (20), + ( + kind: Chest(WorkerPurple0), + stats: ( + protection: Normal(1.0), + ), + ) ), ) diff --git a/assets/common/items/armor/chest/worker_purple_1.ron b/assets/common/items/armor/chest/worker_purple_1.ron index b183b6607b..f6782a1632 100644 --- a/assets/common/items/armor/chest/worker_purple_1.ron +++ b/assets/common/items/armor/chest/worker_purple_1.ron @@ -1,8 +1,12 @@ Item( name: "Purple Worker Shirt", - description: "Chest\n\nArmor: 0\n\n", + description: "Was used by a farmer, until recently.", kind: Armor( - kind: Chest(WorkerPurple1), - stats: (20), + ( + kind: Chest(WorkerPurple1), + stats: ( + protection: Normal(1.0), + ), + ) ), ) diff --git a/assets/common/items/armor/chest/worker_red_0.ron b/assets/common/items/armor/chest/worker_red_0.ron index 0cbdffed32..62f3159ace 100644 --- a/assets/common/items/armor/chest/worker_red_0.ron +++ b/assets/common/items/armor/chest/worker_red_0.ron @@ -1,8 +1,12 @@ Item( name: "Red Worker Shirt", - description: "Chest\n\nArmor: 0\n\n", + description: "Was used by a farmer, until recently.", kind: Armor( - kind: Chest(WorkerRed0), - stats: (20), + ( + kind: Chest(WorkerRed0), + stats: ( + protection: Normal(1.0), + ), + ) ), ) diff --git a/assets/common/items/armor/chest/worker_red_1.ron b/assets/common/items/armor/chest/worker_red_1.ron index 5edb6ef876..a1a3b5eda7 100644 --- a/assets/common/items/armor/chest/worker_red_1.ron +++ b/assets/common/items/armor/chest/worker_red_1.ron @@ -1,8 +1,12 @@ Item( name: "Red Worker Shirt", - description: "Chest\n\nArmor: 0\n\n", + description: "Was used by a farmer, until recently.", kind: Armor( - kind: Chest(WorkerRed1), - stats: (20), + ( + kind: Chest(WorkerRed1), + stats: ( + protection: Normal(1.0), + ), + ) ), ) diff --git a/assets/common/items/armor/chest/worker_yellow_0.ron b/assets/common/items/armor/chest/worker_yellow_0.ron index c79a242550..0d625111f8 100644 --- a/assets/common/items/armor/chest/worker_yellow_0.ron +++ b/assets/common/items/armor/chest/worker_yellow_0.ron @@ -1,8 +1,12 @@ Item( name: "Yellow Worker Shirt", - description: "Chest\n\nArmor: 0\n\n", + description: "Was used by a farmer, until recently.", kind: Armor( - kind: Chest(WorkerYellow0), - stats: (20), + ( + kind: Chest(WorkerYellow0), + stats: ( + protection: Normal(1.0), + ), + ) ), ) diff --git a/assets/common/items/armor/chest/worker_yellow_1.ron b/assets/common/items/armor/chest/worker_yellow_1.ron index 2200e50e91..85d69c6a98 100644 --- a/assets/common/items/armor/chest/worker_yellow_1.ron +++ b/assets/common/items/armor/chest/worker_yellow_1.ron @@ -1,8 +1,12 @@ Item( name: "Yellow Worker Shirt", - description: "Chest\n\nArmor: 0\n\n", + description: "Was used by a farmer, until recently.", kind: Armor( - kind: Chest(WorkerYellow1), - stats: (20), + ( + kind: Chest(WorkerYellow1), + stats: ( + protection: Normal(1.0), + ), + ) ), ) diff --git a/assets/common/items/armor/foot/assassin.ron b/assets/common/items/armor/foot/assassin.ron index 6fd150c332..2b3c47390c 100644 --- a/assets/common/items/armor/foot/assassin.ron +++ b/assets/common/items/armor/foot/assassin.ron @@ -1,8 +1,12 @@ Item( name: "Assassin Boots", - description: "Feet\n\nArmor: 0\n\nOnly the best for a member of the creed.\n\n", + description: "Only the best for a member of the creed.", kind: Armor( - kind: Foot(Assassin), - stats: (20), + ( + kind: Foot(Assassin), + stats: ( + protection: Normal(1.0), + ), + ) ), ) diff --git a/assets/common/items/armor/foot/cloth_blue_0.ron b/assets/common/items/armor/foot/cloth_blue_0.ron index 77c9b299db..239b32ca05 100644 --- a/assets/common/items/armor/foot/cloth_blue_0.ron +++ b/assets/common/items/armor/foot/cloth_blue_0.ron @@ -1,8 +1,12 @@ Item( name: "Blue Linen Boots", - description: "Feet\n\nArmor: 0\n\nSoft and warm\n\n", + description: "Soft and warm.", kind: Armor( - kind: Foot(ClothBlue0), - stats: (20), + ( + kind: Foot(ClothBlue0), + stats: ( + protection: Normal(0.0), + ), + ) ), ) diff --git a/assets/common/items/armor/foot/cloth_green_0.ron b/assets/common/items/armor/foot/cloth_green_0.ron index b2eccab1f2..66586701a0 100644 --- a/assets/common/items/armor/foot/cloth_green_0.ron +++ b/assets/common/items/armor/foot/cloth_green_0.ron @@ -1,8 +1,12 @@ Item( name: "Green Linen Boots", - description: "Feet\n\nArmor: 0\n\nSoft and warm\n\n", + description: "Soft and warm.", kind: Armor( - kind: Foot(ClothGreen0), - stats: (20), + ( + kind: Foot(ClothGreen0), + stats: ( + protection: Normal(0.0), + ), + ) ), ) diff --git a/assets/common/items/armor/foot/cloth_purple_0.ron b/assets/common/items/armor/foot/cloth_purple_0.ron index 77db769d55..fcc1e9b15d 100644 --- a/assets/common/items/armor/foot/cloth_purple_0.ron +++ b/assets/common/items/armor/foot/cloth_purple_0.ron @@ -1,8 +1,12 @@ Item( name: "Purple Linen Boots", - description: "Feet\n\nArmor: 0\n\nSoft and warm\n\n", + description: "Soft and warm.", kind: Armor( - kind: Foot(ClothPurple0), - stats: (20), + ( + kind: Foot(ClothPurple0), + stats: ( + protection: Normal(0.0), + ), + ) ), ) diff --git a/assets/common/items/armor/foot/cultist_boots.ron b/assets/common/items/armor/foot/cultist_boots.ron index 70d134e5fe..d24453912e 100644 --- a/assets/common/items/armor/foot/cultist_boots.ron +++ b/assets/common/items/armor/foot/cultist_boots.ron @@ -1,8 +1,12 @@ Item( name: "Cultist Boots", - description: "Feet\n\nArmor: 0\n\n", + description: "Ceremonial attire used by members.", kind: Armor( - kind: Foot(Cultist), - stats: (20), + ( + kind: Foot(Cultist), + stats: ( + protection: Normal(1.0), + ), + ) ), ) diff --git a/assets/common/items/armor/foot/druid.ron b/assets/common/items/armor/foot/druid.ron index ceb8f5979f..51cd2072d4 100644 --- a/assets/common/items/armor/foot/druid.ron +++ b/assets/common/items/armor/foot/druid.ron @@ -1,8 +1,12 @@ Item( name: "Druid's Slippers", - description: "For treading softly through the woods.\n\n", + description: "For treading softly through the woods.", kind: Armor( - kind: Foot(Druid), - stats: (20), + ( + kind: Foot(Druid), + stats: ( + protection: Normal(1.0), + ), + ) ), ) diff --git a/assets/common/items/armor/foot/jackalope_slippers.ron b/assets/common/items/armor/foot/jackalope_slippers.ron index bd0804c5d3..751dce12a3 100644 --- a/assets/common/items/armor/foot/jackalope_slippers.ron +++ b/assets/common/items/armor/foot/jackalope_slippers.ron @@ -1,8 +1,12 @@ Item( name: "Fluffy Jackalope Slippers", - description: "So warm and cozy!\n\n", + description: "So warm and cozy!", kind: Armor( - kind: Foot(JackalopeSlips), - stats: (20), + ( + kind: Foot(JackalopeSlips), + stats: ( + protection: Normal(0.0), + ), + ) ), ) diff --git a/assets/common/items/armor/foot/leather_0.ron b/assets/common/items/armor/foot/leather_0.ron index 7e811efc3a..bd508944de 100644 --- a/assets/common/items/armor/foot/leather_0.ron +++ b/assets/common/items/armor/foot/leather_0.ron @@ -1,8 +1,12 @@ Item( name: "Swift Boots", - description: "Feet\n\nArmor: 0\n\nSwift like the wind.\nWon't make you run faster.\n\n", + description: "Swift like the wind.", kind: Armor( - kind: Foot(Leather0), - stats: (20), + ( + kind: Foot(Leather0), + stats: ( + protection: Normal(2.0), + ), + ) ), ) diff --git a/assets/common/items/armor/foot/leather_2.ron b/assets/common/items/armor/foot/leather_2.ron index ad91562bb3..6fe2ce0c6f 100644 --- a/assets/common/items/armor/foot/leather_2.ron +++ b/assets/common/items/armor/foot/leather_2.ron @@ -1,8 +1,12 @@ Item( name: "Leather Boots", - description: "Boots made of simple leather", + description: "Boots made of simple leather.", kind: Armor( - kind: Foot(Leather2), - stats: (20), + ( + kind: Foot(Leather2), + stats: ( + protection: Normal(2.0), + ), + ) ), ) diff --git a/assets/common/items/armor/foot/plate_0.ron b/assets/common/items/armor/foot/plate_0.ron index 21dc0fe27c..fd9c41bb15 100644 --- a/assets/common/items/armor/foot/plate_0.ron +++ b/assets/common/items/armor/foot/plate_0.ron @@ -1,8 +1,12 @@ Item( name: "Iron Feet", - description: "Feet\n\nArmor: 0\n\n", + description: "Boots forged from iron.", kind: Armor( - kind: Foot(Plate0), - stats: (20), + ( + kind: Foot(Plate0), + stats: ( + protection: Normal(4.0), + ), + ) ), ) diff --git a/assets/common/items/armor/foot/steel_0.ron b/assets/common/items/armor/foot/steel_0.ron index ccd47f75fb..5b39db22e4 100644 --- a/assets/common/items/armor/foot/steel_0.ron +++ b/assets/common/items/armor/foot/steel_0.ron @@ -1,8 +1,12 @@ Item( name: "Steel Boots", - description: "Boots plated in steel", + description: "Boots forged from steel.", kind: Armor( - kind: Foot(Steel0), - stats: (20), + ( + kind: Foot(Steel0), + stats: ( + protection: Normal(5.0), + ), + ) ), ) diff --git a/assets/common/items/armor/foot/twig.ron b/assets/common/items/armor/foot/twig.ron index e367a857dd..742c7b1cc9 100644 --- a/assets/common/items/armor/foot/twig.ron +++ b/assets/common/items/armor/foot/twig.ron @@ -1,8 +1,12 @@ Item( name: "Twig Boots", - description: "Boots woven from twigs.\n\n", + description: "Boots woven from twigs.", kind: Armor( - kind: Foot(Twig), - stats: (20), + ( + kind: Foot(Twig), + stats: ( + protection: Normal(3.0), + ), + ) ), ) diff --git a/assets/common/items/armor/foot/twigsflowers.ron b/assets/common/items/armor/foot/twigsflowers.ron index fda0a894fc..ec1918a11b 100644 --- a/assets/common/items/armor/foot/twigsflowers.ron +++ b/assets/common/items/armor/foot/twigsflowers.ron @@ -1,8 +1,12 @@ Item( name: "Flowery Boots", - description: "Boots woven from twigs and flowers.\n\n", + description: "Boots woven from twigs and flowers.", kind: Armor( - kind: Foot(Twigsflowers), - stats: (20), + ( + kind: Foot(Twigsflowers), + stats: ( + protection: Normal(3.0), + ), + ) ), ) diff --git a/assets/common/items/armor/foot/twigsleaves.ron b/assets/common/items/armor/foot/twigsleaves.ron index 7aed59cedd..7dfa97ac34 100644 --- a/assets/common/items/armor/foot/twigsleaves.ron +++ b/assets/common/items/armor/foot/twigsleaves.ron @@ -1,8 +1,12 @@ Item( name: "Leafy Boots", - description: "Boots woven from twigs and leaves.\n\n", + description: "Boots woven from twigs and leaves.", kind: Armor( - kind: Foot(Twigsleaves), - stats: (20), + ( + kind: Foot(Twigsleaves), + stats: ( + protection: Normal(3.0), + ), + ) ), ) diff --git a/assets/common/items/armor/hand/assassin.ron b/assets/common/items/armor/hand/assassin.ron index 8e923a13d4..9b635f7a0d 100644 --- a/assets/common/items/armor/hand/assassin.ron +++ b/assets/common/items/armor/hand/assassin.ron @@ -1,8 +1,12 @@ Item( name: "Assassin Gloves", - description: "Hands\n\nArmor: 0\n\nOnly the best for a member of the creed.\n\n", + description: "Only the best for a member of the creed.", kind: Armor( - kind: Hand(Assassin), - stats: (20), + ( + kind: Hand(Assassin), + stats: ( + protection: Normal(2.0), + ), + ) ), ) diff --git a/assets/common/items/armor/hand/cloth_blue_0.ron b/assets/common/items/armor/hand/cloth_blue_0.ron index f692f8123f..9fcc19d577 100644 --- a/assets/common/items/armor/hand/cloth_blue_0.ron +++ b/assets/common/items/armor/hand/cloth_blue_0.ron @@ -1,8 +1,12 @@ Item( name: "Blue Linen Wrists", - description: "Hands\n\nArmor: 0\n\n", + description: "A strip of cloth.", kind: Armor( - kind: Hand(ClothBlue0), - stats: (20), + ( + kind: Hand(ClothBlue0), + stats: ( + protection: Normal(0.0), + ), + ) ), ) diff --git a/assets/common/items/armor/hand/cloth_green_0.ron b/assets/common/items/armor/hand/cloth_green_0.ron index 5a20af9f12..face79a1d4 100644 --- a/assets/common/items/armor/hand/cloth_green_0.ron +++ b/assets/common/items/armor/hand/cloth_green_0.ron @@ -1,8 +1,12 @@ Item( name: "Green Linen Wrists", - description: "Hands\n\nArmor: 0\n\n", + description: "A strip of cloth.", kind: Armor( - kind: Hand(ClothGreen0), - stats: (20), + ( + kind: Hand(ClothGreen0), + stats: ( + protection: Normal(0.0), + ), + ) ), ) diff --git a/assets/common/items/armor/hand/cloth_purple_0.ron b/assets/common/items/armor/hand/cloth_purple_0.ron index f4c99713f6..5946dddd2f 100644 --- a/assets/common/items/armor/hand/cloth_purple_0.ron +++ b/assets/common/items/armor/hand/cloth_purple_0.ron @@ -1,8 +1,12 @@ Item( name: "Purple Silk Wrists", - description: "Hands\n\nArmor: 0\n\n", + description: "A strip of cloth.", kind: Armor( - kind: Hand(ClothPurple0), - stats: (20), + ( + kind: Hand(ClothPurple0), + stats: ( + protection: Normal(0.0), + ), + ) ), ) diff --git a/assets/common/items/armor/hand/cultist_hands_blue.ron b/assets/common/items/armor/hand/cultist_hands_blue.ron index c6d3076b58..32875de0cd 100644 --- a/assets/common/items/armor/hand/cultist_hands_blue.ron +++ b/assets/common/items/armor/hand/cultist_hands_blue.ron @@ -1,8 +1,12 @@ Item( name: "Blue Cultist Gloves", - description: "Hands\n\nArmor: 0\n\n", + description: "Ceremonial attire used by members.", kind: Armor( - kind: Hand(CultistBlue), - stats: (20), + ( + kind: Hand(CultistBlue), + stats: ( + protection: Normal(2.0), + ), + ) ), ) diff --git a/assets/common/items/armor/hand/cultist_hands_purple.ron b/assets/common/items/armor/hand/cultist_hands_purple.ron index fb89f73edd..3df26460d4 100644 --- a/assets/common/items/armor/hand/cultist_hands_purple.ron +++ b/assets/common/items/armor/hand/cultist_hands_purple.ron @@ -1,8 +1,12 @@ Item( name: "Purple Cultist Gloves", - description: "Hands\n\nArmor: 0\n\n", + description: "Ceremonial attire used by members.", kind: Armor( - kind: Hand(CultistPurple), - stats: (20), + ( + kind: Hand(CultistPurple), + stats: ( + protection: Normal(2.0), + ), + ) ), ) diff --git a/assets/common/items/armor/hand/druid.ron b/assets/common/items/armor/hand/druid.ron index 63400b0f1f..ddb12753a7 100644 --- a/assets/common/items/armor/hand/druid.ron +++ b/assets/common/items/armor/hand/druid.ron @@ -1,8 +1,12 @@ Item( name: "Druid's Gloves", - description: "Soft, strong, and flexible.\n\n", + description: "Soft, strong, and flexible.", kind: Armor( - kind: Hand(Druid), - stats: (20), + ( + kind: Hand(Druid), + stats: ( + protection: Normal(2.0), + ), + ) ), ) diff --git a/assets/common/items/armor/hand/leather_0.ron b/assets/common/items/armor/hand/leather_0.ron index d19024f3e7..050623f3c3 100644 --- a/assets/common/items/armor/hand/leather_0.ron +++ b/assets/common/items/armor/hand/leather_0.ron @@ -1,8 +1,12 @@ Item( name: "Swift Gloves", - description: "Hands\n\nArmor: 0\n\nSwift like the wind.\n\n", + description: "Swift like the wind.", kind: Armor( - kind: Hand(Leather0), - stats: (20), + ( + kind: Hand(Leather0), + stats: ( + protection: Normal(4.0), + ), + ) ), ) diff --git a/assets/common/items/armor/hand/leather_2.ron b/assets/common/items/armor/hand/leather_2.ron index 22b52d99a1..639b1ba37a 100644 --- a/assets/common/items/armor/hand/leather_2.ron +++ b/assets/common/items/armor/hand/leather_2.ron @@ -1,8 +1,12 @@ Item( name: "Leather Gloves", - description: "Gloves made of simple leather", + description: "Gloves made of simple leather.", kind: Armor( - kind: Hand(Leather2), - stats: (20), + ( + kind: Hand(Leather2), + stats: ( + protection: Normal(4.0), + ), + ) ), ) diff --git a/assets/common/items/armor/hand/plate_0.ron b/assets/common/items/armor/hand/plate_0.ron index 516aab8625..20242b87ed 100644 --- a/assets/common/items/armor/hand/plate_0.ron +++ b/assets/common/items/armor/hand/plate_0.ron @@ -1,8 +1,12 @@ Item( name: "Iron Handguards", - description: "Hands\n\nArmor: 0\n\n", + description: "Gauntlets forged from iron.", kind: Armor( - kind: Hand(Plate0), - stats: (20), + ( + kind: Hand(Plate0), + stats: ( + protection: Normal(8.0), + ), + ) ), ) diff --git a/assets/common/items/armor/hand/steel_0.ron b/assets/common/items/armor/hand/steel_0.ron index ad7e237adc..6978576802 100644 --- a/assets/common/items/armor/hand/steel_0.ron +++ b/assets/common/items/armor/hand/steel_0.ron @@ -1,8 +1,12 @@ Item( name: "Steel Gauntlets", - description: "Gauntlets made of steel", + description: "Gauntlets forged from steel.", kind: Armor( - kind: Hand(Steel0), - stats: (20), + ( + kind: Hand(Steel0), + stats: ( + protection: Normal(10.0), + ), + ) ), ) diff --git a/assets/common/items/armor/hand/twig.ron b/assets/common/items/armor/hand/twig.ron index bf61fe0209..9cb73a911b 100644 --- a/assets/common/items/armor/hand/twig.ron +++ b/assets/common/items/armor/hand/twig.ron @@ -1,8 +1,12 @@ Item( name: "Twig Wraps", - description: "Handwraps woven from twigs.\n\n", + description: "Handwraps woven from twigs.", kind: Armor( - kind: Hand(Twig), - stats: (20), + ( + kind: Hand(Twig), + stats: ( + protection: Normal(6.0), + ), + ) ), ) diff --git a/assets/common/items/armor/hand/twigsflowers.ron b/assets/common/items/armor/hand/twigsflowers.ron index 68cbc953aa..04a6f4b098 100644 --- a/assets/common/items/armor/hand/twigsflowers.ron +++ b/assets/common/items/armor/hand/twigsflowers.ron @@ -1,8 +1,12 @@ Item( name: "Flowery Wraps", - description: "Handwraps woven from twigs and flowers.\n\n", + description: "Handwraps woven from twigs and flowers.", kind: Armor( - kind: Hand(Twigsflowers), - stats: (20), + ( + kind: Hand(Twigsflowers), + stats: ( + protection: Normal(6.0), + ), + ) ), ) diff --git a/assets/common/items/armor/hand/twigsleaves.ron b/assets/common/items/armor/hand/twigsleaves.ron index 13337ae564..8226c3d103 100644 --- a/assets/common/items/armor/hand/twigsleaves.ron +++ b/assets/common/items/armor/hand/twigsleaves.ron @@ -1,8 +1,12 @@ Item( name: "Leafy Wraps", - description: "Handwraps woven from twigs and leaves.\n\n", + description: "Handwraps woven from twigs and leaves.", kind: Armor( - kind: Hand(Twigsleaves), - stats: (20), + ( + kind: Hand(Twigsleaves), + stats: ( + protection: Normal(6.0), + ), + ) ), ) diff --git a/assets/common/items/armor/head/assa_mask_0.ron b/assets/common/items/armor/head/assa_mask_0.ron index 236ea82ef3..2ee38ca25e 100644 --- a/assets/common/items/armor/head/assa_mask_0.ron +++ b/assets/common/items/armor/head/assa_mask_0.ron @@ -1,8 +1,12 @@ Item( name: "Dark Assassin Mask", - description: "Head\n\nArmor: 0\n\n", + description: "Used to obscure your face.", kind: Armor( - kind: Head(AssaMask0), - stats: (20), + ( + kind: Head(AssaMask0), + stats: ( + protection: Normal(0.0), + ), + ) ), ) diff --git a/assets/common/items/armor/head/leather_0.ron b/assets/common/items/armor/head/leather_0.ron index c568860917..9ce5ba6814 100644 --- a/assets/common/items/armor/head/leather_0.ron +++ b/assets/common/items/armor/head/leather_0.ron @@ -1,8 +1,12 @@ Item( name: "Swift Leather Cap", - description: "Head\n\nArmor: 0\n\n", + description: "Swift like the wind.", kind: Armor( - kind: Head(Leather0), - stats: (20), + ( + kind: Head(Leather0), + stats: ( + protection: Normal(0.0), + ), + ) ), ) diff --git a/assets/common/items/armor/neck/neck_0.ron b/assets/common/items/armor/neck/neck_0.ron index d170113336..e8b25c6f58 100644 --- a/assets/common/items/armor/neck/neck_0.ron +++ b/assets/common/items/armor/neck/neck_0.ron @@ -1,8 +1,12 @@ Item( name: "Plain Necklace", - description: "Neck\n\nArmor: 0\n\n", + description: "It's become tarnished with age.", kind: Armor( - kind: Neck(Neck0), - stats: (20), + ( + kind: Neck(Neck0), + stats: ( + protection: Normal(0.0), + ), + ) ), ) diff --git a/assets/common/items/armor/pants/assassin.ron b/assets/common/items/armor/pants/assassin.ron index 3fce548e9a..be00f18aa6 100644 --- a/assets/common/items/armor/pants/assassin.ron +++ b/assets/common/items/armor/pants/assassin.ron @@ -1,8 +1,12 @@ Item( name: "Assassin Pants", - description: "Legs\n\nArmor: 0\n\nOnly the best for a member of the creed.\n\n", + description: "Only the best for a member of the creed.", kind: Armor( - kind: Pants(Assassin), - stats: (20), + ( + kind: Pants(Assassin), + stats: ( + protection: Normal(5.0), + ), + ) ), ) diff --git a/assets/common/items/armor/pants/cloth_blue_0.ron b/assets/common/items/armor/pants/cloth_blue_0.ron index 2b0dc7e163..52ebc0c3a5 100644 --- a/assets/common/items/armor/pants/cloth_blue_0.ron +++ b/assets/common/items/armor/pants/cloth_blue_0.ron @@ -1,8 +1,12 @@ Item( name: "Blue Linen Skirt", - description: "Legs\n\nArmor: 0\n\n", + description: "A skirt made from linen.", kind: Armor( - kind: Pants(ClothBlue0), - stats: (20), + ( + kind: Pants(ClothBlue0), + stats: ( + protection: Normal(1.0), + ), + ) ), ) diff --git a/assets/common/items/armor/pants/cloth_green_0.ron b/assets/common/items/armor/pants/cloth_green_0.ron index b184717ccf..364e058cee 100644 --- a/assets/common/items/armor/pants/cloth_green_0.ron +++ b/assets/common/items/armor/pants/cloth_green_0.ron @@ -1,8 +1,12 @@ Item( name: "Green Linen Skirt", - description: "Legs\n\nArmor: 0\n\n", + description: "A skirt made from linen.", kind: Armor( - kind: Pants(ClothGreen0), - stats: (20), + ( + kind: Pants(ClothGreen0), + stats: ( + protection: Normal(1.0), + ), + ) ), ) diff --git a/assets/common/items/armor/pants/cloth_purple_0.ron b/assets/common/items/armor/pants/cloth_purple_0.ron index f37662b7db..efcd70f574 100644 --- a/assets/common/items/armor/pants/cloth_purple_0.ron +++ b/assets/common/items/armor/pants/cloth_purple_0.ron @@ -1,8 +1,12 @@ Item( name: "Purple Linen Skirt", - description: "Legs\n\nArmor: 0\n\n", + description: "A skirt made from linen.", kind: Armor( - kind: Pants(ClothPurple0), - stats: (20), + ( + kind: Pants(ClothPurple0), + stats: ( + protection: Normal(1.0), + ), + ) ), ) diff --git a/assets/common/items/armor/pants/cultist_legs_blue.ron b/assets/common/items/armor/pants/cultist_legs_blue.ron index 5ad8a8c25a..33fe4c2826 100644 --- a/assets/common/items/armor/pants/cultist_legs_blue.ron +++ b/assets/common/items/armor/pants/cultist_legs_blue.ron @@ -1,8 +1,12 @@ Item( name: "Blue Cultist Skirt", - description: "Legs\n\nArmor: 0\n\n", + description: "Ceremonial attire used by members.", kind: Armor( - kind: Pants(CultistBlue), - stats: (20), + ( + kind: Pants(CultistBlue), + stats: ( + protection: Normal(3.0), + ), + ) ), ) diff --git a/assets/common/items/armor/pants/cultist_legs_purple.ron b/assets/common/items/armor/pants/cultist_legs_purple.ron index 39550047d6..a6e7cdb7a5 100644 --- a/assets/common/items/armor/pants/cultist_legs_purple.ron +++ b/assets/common/items/armor/pants/cultist_legs_purple.ron @@ -1,8 +1,12 @@ Item( name: "Purple Cultist Skirt", - description: "Legs\n\nArmor: 0\n\n", + description: "Ceremonial attire used by members.", kind: Armor( - kind: Pants(CultistPurple), - stats: (20), + ( + kind: Pants(CultistPurple), + stats: ( + protection: Normal(3.0), + ), + ) ), ) diff --git a/assets/common/items/armor/pants/druid.ron b/assets/common/items/armor/pants/druid.ron index d4b635d4ff..f1e35bac41 100644 --- a/assets/common/items/armor/pants/druid.ron +++ b/assets/common/items/armor/pants/druid.ron @@ -1,8 +1,12 @@ Item( name: "Druid's Kilt", - description: "Feel the breeze!\n\n", + description: "Feel the breeze!", kind: Armor( - kind: Pants(Druid), - stats: (20), + ( + kind: Pants(Druid), + stats: ( + protection: Normal(4.0), + ), + ) ), ) diff --git a/assets/common/items/armor/pants/hunting.ron b/assets/common/items/armor/pants/hunting.ron index bd30d1801f..16dbf0d1c8 100644 --- a/assets/common/items/armor/pants/hunting.ron +++ b/assets/common/items/armor/pants/hunting.ron @@ -1,8 +1,12 @@ Item( name: "Hunting Pants", - description: "Crafted from soft, supple leather\n\n", + description: "Crafted from soft, supple leather.", kind: Armor( - kind: Pants(Hunting), - stats: (20), + ( + kind: Pants(Hunting), + stats: ( + protection: Normal(8.0), + ), + ) ), ) diff --git a/assets/common/items/armor/pants/leather_0.ron b/assets/common/items/armor/pants/leather_0.ron index 8eb99060d7..cf7b058a02 100644 --- a/assets/common/items/armor/pants/leather_0.ron +++ b/assets/common/items/armor/pants/leather_0.ron @@ -1,8 +1,12 @@ Item( name: "Swift Pants", - description: "Legs\n\nArmor: 0\n\n", + description: "Swift like the wind.", kind: Armor( - kind: Pants(Leather0), - stats: (20), + ( + kind: Pants(Leather0), + stats: ( + protection: Normal(8.0), + ), + ) ), ) diff --git a/assets/common/items/armor/pants/leather_2.ron b/assets/common/items/armor/pants/leather_2.ron index 31b55c6f62..05d4fcf9df 100644 --- a/assets/common/items/armor/pants/leather_2.ron +++ b/assets/common/items/armor/pants/leather_2.ron @@ -1,8 +1,12 @@ Item( name: "Leather Leg Armour", - description: "Leg armour made of simple leather", + description: "Leg armour made of simple leather.", kind: Armor( - kind: Pants(Leather2), - stats: (20), + ( + kind: Pants(Leather2), + stats: ( + protection: Normal(8.0), + ), + ) ), ) diff --git a/assets/common/items/armor/pants/plate_green_0.ron b/assets/common/items/armor/pants/plate_green_0.ron index 3194cac534..d1167a6934 100644 --- a/assets/common/items/armor/pants/plate_green_0.ron +++ b/assets/common/items/armor/pants/plate_green_0.ron @@ -1,8 +1,12 @@ Item( name: "Iron Legguards", - description: "Legs\n\nArmor: 0\n\n", + description: "Greaves forged from iron.", kind: Armor( - kind: Pants(PlateGreen0), - stats: (20), + ( + kind: Pants(PlateGreen0), + stats: ( + protection: Normal(16.0), + ), + ) ), ) diff --git a/assets/common/items/armor/pants/steel_0.ron b/assets/common/items/armor/pants/steel_0.ron index aed385a94d..7349351884 100644 --- a/assets/common/items/armor/pants/steel_0.ron +++ b/assets/common/items/armor/pants/steel_0.ron @@ -1,8 +1,12 @@ Item( name: "Steel Chausses", - description: "Leg armour made of steel plates", + description: "Greaves forged from steel.", kind: Armor( - kind: Pants(Steel0), - stats: (20), + ( + kind: Pants(Steel0), + stats: ( + protection: Normal(20.0), + ), + ) ), ) diff --git a/assets/common/items/armor/pants/twig.ron b/assets/common/items/armor/pants/twig.ron index 0cbb923d0e..8dfa916027 100644 --- a/assets/common/items/armor/pants/twig.ron +++ b/assets/common/items/armor/pants/twig.ron @@ -1,8 +1,12 @@ Item( name: "Twig Pants", - description: "Pants woven from twigs. Chafey!\n\n", + description: "Pants woven from twigs. Chafey!", kind: Armor( - kind: Pants(Twig), - stats: (20), + ( + kind: Pants(Twig), + stats: ( + protection: Normal(12.0), + ), + ) ), ) diff --git a/assets/common/items/armor/pants/twigsflowers.ron b/assets/common/items/armor/pants/twigsflowers.ron index abed6274eb..2af4112492 100644 --- a/assets/common/items/armor/pants/twigsflowers.ron +++ b/assets/common/items/armor/pants/twigsflowers.ron @@ -1,8 +1,12 @@ Item( name: "Flowery Pants", - description: "Pants woven from twigs and flowers. Fragrant!\n\n", + description: "Pants woven from twigs and flowers. Fragrant!", kind: Armor( - kind: Pants(Twigsflowers), - stats: (20), + ( + kind: Pants(Twigsflowers), + stats: ( + protection: Normal(12.0), + ), + ) ), ) diff --git a/assets/common/items/armor/pants/twigsleaves.ron b/assets/common/items/armor/pants/twigsleaves.ron index 803993b8d0..14065ef14c 100644 --- a/assets/common/items/armor/pants/twigsleaves.ron +++ b/assets/common/items/armor/pants/twigsleaves.ron @@ -1,8 +1,12 @@ Item( name: "Leafy Pants", - description: "Pants woven from twigs and leaves. Slightly less chafey than the twig pants!\n\n", + description: "Pants woven from twigs and leaves. Slightly less chafey than the twig pants!", kind: Armor( - kind: Pants(Twigsleaves), - stats: (20), + ( + kind: Pants(Twigsleaves), + stats: ( + protection: Normal(12.0), + ), + ) ), ) diff --git a/assets/common/items/armor/pants/worker_blue_0.ron b/assets/common/items/armor/pants/worker_blue_0.ron index 8fc56c4ee6..bd9f2a7916 100644 --- a/assets/common/items/armor/pants/worker_blue_0.ron +++ b/assets/common/items/armor/pants/worker_blue_0.ron @@ -1,8 +1,12 @@ Item( name: "Blue Worker Pants", - description: "Legs\n\nArmor: 0\n\n", + description: "Pants used by a farmer, until recently.", kind: Armor( - kind: Pants(WorkerBlue0), - stats: (20), + ( + kind: Pants(WorkerBlue0), + stats: ( + protection: Normal(1.0), + ), + ) ), ) diff --git a/assets/common/items/armor/ring/ring_0.ron b/assets/common/items/armor/ring/ring_0.ron index d0448912f3..daa0914bbd 100644 --- a/assets/common/items/armor/ring/ring_0.ron +++ b/assets/common/items/armor/ring/ring_0.ron @@ -1,8 +1,12 @@ Item( name: "Scratched Ring", - description: "Ring\n\nBarely fits your finger.\n\n", + description: "Barely fits your finger.", kind: Armor( - kind: Ring(Ring0), - stats: (20), + ( + kind: Ring(Ring0), + stats: ( + protection: Normal(0.0), + ), + ) ), ) diff --git a/assets/common/items/armor/shoulder/assassin.ron b/assets/common/items/armor/shoulder/assassin.ron index bff3ac8131..a3dbee15aa 100644 --- a/assets/common/items/armor/shoulder/assassin.ron +++ b/assets/common/items/armor/shoulder/assassin.ron @@ -1,8 +1,12 @@ Item( name: "Assassin Shoulder Guard", - description: "Shoulders\n\nArmor: 0\n\nOnly the best for a member of the creed.\n\n", + description: "Only the best for a member of the creed.", kind: Armor( - kind: Shoulder(Assassin), - stats: (20), + ( + kind: Shoulder(Assassin), + stats: ( + protection: Normal(3.0), + ), + ) ), ) diff --git a/assets/common/items/armor/shoulder/cloth_blue_0.ron b/assets/common/items/armor/shoulder/cloth_blue_0.ron index 7c19790bd8..432ae4af55 100644 --- a/assets/common/items/armor/shoulder/cloth_blue_0.ron +++ b/assets/common/items/armor/shoulder/cloth_blue_0.ron @@ -1,8 +1,12 @@ Item( name: "Blue Linen Coat", - description: "Shoulders\n\nArmor: 0\n\n", + description: "A warm coat.", kind: Armor( - kind: Shoulder(ClothBlue0), - stats: (20), + ( + kind: Shoulder(ClothBlue0), + stats: ( + protection: Normal(0.0), + ), + ) ), ) diff --git a/assets/common/items/armor/shoulder/cloth_blue_1.ron b/assets/common/items/armor/shoulder/cloth_blue_1.ron index c63a33dbd3..d435fc8ebf 100644 --- a/assets/common/items/armor/shoulder/cloth_blue_1.ron +++ b/assets/common/items/armor/shoulder/cloth_blue_1.ron @@ -1,8 +1,12 @@ Item( name: "Blue Cloth Pads", - description: "Simple shoulderpads made from blue cloth.\n\n", + description: "Simple shoulderpads made from blue cloth.", kind: Armor( - kind: Shoulder(ClothBlue1), - stats: (20), + ( + kind: Shoulder(ClothBlue1), + stats: ( + protection: Normal(0.0), + ), + ) ), ) diff --git a/assets/common/items/armor/shoulder/cloth_green_0.ron b/assets/common/items/armor/shoulder/cloth_green_0.ron index b3362a8d82..d9eaa35359 100644 --- a/assets/common/items/armor/shoulder/cloth_green_0.ron +++ b/assets/common/items/armor/shoulder/cloth_green_0.ron @@ -1,8 +1,12 @@ Item( name: "Green Linen Coat", - description: "Shoulders\n\nArmor: 0\n\n", + description: "A warm coat.", kind: Armor( - kind: Shoulder(ClothGreen0), - stats: (20), + ( + kind: Shoulder(ClothGreen0), + stats: ( + protection: Normal(0.0), + ), + ) ), ) diff --git a/assets/common/items/armor/shoulder/cloth_purple_0.ron b/assets/common/items/armor/shoulder/cloth_purple_0.ron index ecf434a9b3..10a73ef105 100644 --- a/assets/common/items/armor/shoulder/cloth_purple_0.ron +++ b/assets/common/items/armor/shoulder/cloth_purple_0.ron @@ -1,8 +1,12 @@ Item( name: "Purple Linen Coat", - description: "Shoulders\n\nArmor: 0\n\n", + description: "A warm coat.", kind: Armor( - kind: Shoulder(ClothPurple0), - stats: (20), + ( + kind: Shoulder(ClothPurple0), + stats: ( + protection: Normal(0.0), + ), + ) ), ) diff --git a/assets/common/items/armor/shoulder/cultist_shoulder_blue.ron b/assets/common/items/armor/shoulder/cultist_shoulder_blue.ron index 1e4d3965c8..a7df2169aa 100644 --- a/assets/common/items/armor/shoulder/cultist_shoulder_blue.ron +++ b/assets/common/items/armor/shoulder/cultist_shoulder_blue.ron @@ -1,8 +1,12 @@ Item( name: "Blue Cultist Mantle", - description: "Shoulders\n\nArmor: 0\n\nA strong shoulder to lean on.\n\n", + description: "Ceremonial attire used by members.", kind: Armor( - kind: Shoulder(CultistBlue), - stats: (20), + ( + kind: Shoulder(CultistBlue), + stats: ( + protection: Normal(3.0), + ), + ) ), ) diff --git a/assets/common/items/armor/shoulder/cultist_shoulder_purple.ron b/assets/common/items/armor/shoulder/cultist_shoulder_purple.ron index 095d218bf3..3d01973bff 100644 --- a/assets/common/items/armor/shoulder/cultist_shoulder_purple.ron +++ b/assets/common/items/armor/shoulder/cultist_shoulder_purple.ron @@ -1,8 +1,12 @@ Item( name: "Purple Cultist Mantle", - description: "Shoulders\n\nArmor: 0\n\nA strong shoulder to lean on.\n\n", + description: "Ceremonial attire used by members.", kind: Armor( - kind: Shoulder(CultistPurple), - stats: (20), + ( + kind: Shoulder(CultistPurple), + stats: ( + protection: Normal(3.0), + ), + ) ), ) diff --git a/assets/common/items/armor/shoulder/druidshoulder.ron b/assets/common/items/armor/shoulder/druidshoulder.ron index b17c658c28..7f88373304 100644 --- a/assets/common/items/armor/shoulder/druidshoulder.ron +++ b/assets/common/items/armor/shoulder/druidshoulder.ron @@ -1,8 +1,12 @@ Item( name: "Druid Shoulders", - description: "Forged for protectors of the wild.\n\n", + description: "Forged for protectors of the wild.", kind: Armor( - kind: Shoulder(DruidShoulder), - stats: (20), + ( + kind: Shoulder(DruidShoulder), + stats: ( + protection: Normal(3.0), + ), + ) ), ) diff --git a/assets/common/items/armor/shoulder/iron_spikes.ron b/assets/common/items/armor/shoulder/iron_spikes.ron index a2c95c9360..a253e7a276 100644 --- a/assets/common/items/armor/shoulder/iron_spikes.ron +++ b/assets/common/items/armor/shoulder/iron_spikes.ron @@ -1,8 +1,12 @@ Item( name: "Iron Spiked Pauldrons", - description: "Iron shoulder pads with spikes attached.\n\n", + description: "Iron shoulder pads with spikes attached.", kind: Armor( - kind: Shoulder(IronSpikes), - stats: (20), + ( + kind: Shoulder(IronSpikes), + stats: ( + protection: Normal(12.0), + ), + ) ), ) diff --git a/assets/common/items/armor/shoulder/leather_0.ron b/assets/common/items/armor/shoulder/leather_0.ron index 82e18d2d45..bbdc133063 100644 --- a/assets/common/items/armor/shoulder/leather_0.ron +++ b/assets/common/items/armor/shoulder/leather_0.ron @@ -1,8 +1,12 @@ Item( name: "Leather Pauldrons", - description: "Shoulders\n\nArmor: 0\n\n", + description: "Shoulder pads made of leather.", kind: Armor( - kind: Shoulder(Leather0), - stats: (20), + ( + kind: Shoulder(Leather0), + stats: ( + protection: Normal(6.0), + ), + ) ), ) diff --git a/assets/common/items/armor/shoulder/leather_1.ron b/assets/common/items/armor/shoulder/leather_1.ron index 1237ac242c..92c9047c26 100644 --- a/assets/common/items/armor/shoulder/leather_1.ron +++ b/assets/common/items/armor/shoulder/leather_1.ron @@ -1,8 +1,12 @@ Item( name: "Swift Shoulderpads", - description: "Shoulders\n\nArmor: 0\n\nSwift like the wind.\n\n", + description: "Swift like the wind.", kind: Armor( - kind: Shoulder(Leather1), - stats: (20), + ( + kind: Shoulder(Leather1), + stats: ( + protection: Normal(6.0), + ), + ) ), ) diff --git a/assets/common/items/armor/shoulder/leather_2.ron b/assets/common/items/armor/shoulder/leather_2.ron index 883e07d95d..2a8531895e 100644 --- a/assets/common/items/armor/shoulder/leather_2.ron +++ b/assets/common/items/armor/shoulder/leather_2.ron @@ -1,8 +1,12 @@ Item( name: "Leather Shoulder Pad", - description: "A simple shoulder pad made of leather", + description: "A simple shoulder pad made of leather.", kind: Armor( - kind: Shoulder(Leather2), - stats: (20), + ( + kind: Shoulder(Leather2), + stats: ( + protection: Normal(6.0), + ), + ) ), ) diff --git a/assets/common/items/armor/shoulder/leather_iron_0.ron b/assets/common/items/armor/shoulder/leather_iron_0.ron index f19799ca90..6a26e9c7c5 100644 --- a/assets/common/items/armor/shoulder/leather_iron_0.ron +++ b/assets/common/items/armor/shoulder/leather_iron_0.ron @@ -1,8 +1,12 @@ Item( name: "Iron and Leather Spaulders", - description: "Robust spaulders made from iron and leather.\n\n", + description: "Robust spaulders made from iron and leather.", kind: Armor( - kind: Shoulder(IronLeather0), - stats: (20), + ( + kind: Shoulder(IronLeather0), + stats: ( + protection: Normal(9.0), + ), + ) ), ) diff --git a/assets/common/items/armor/shoulder/leather_iron_1.ron b/assets/common/items/armor/shoulder/leather_iron_1.ron index aaac593da4..2f2bc043fa 100644 --- a/assets/common/items/armor/shoulder/leather_iron_1.ron +++ b/assets/common/items/armor/shoulder/leather_iron_1.ron @@ -1,8 +1,12 @@ Item( name: "Iron and Leather Spaulders", - description: "Robust spaulders made from iron and leather.\n\n", + description: "Robust spaulders made from iron and leather.", kind: Armor( - kind: Shoulder(IronLeather1), - stats: (20), + ( + kind: Shoulder(IronLeather1), + stats: ( + protection: Normal(9.0), + ), + ) ), ) diff --git a/assets/common/items/armor/shoulder/leather_iron_2.ron b/assets/common/items/armor/shoulder/leather_iron_2.ron index c939e0e0e7..653597c82e 100644 --- a/assets/common/items/armor/shoulder/leather_iron_2.ron +++ b/assets/common/items/armor/shoulder/leather_iron_2.ron @@ -1,8 +1,12 @@ Item( name: "Iron and Leather Spaulders", - description: "Robust spaulders made from iron and leather.\n\n", + description: "Robust spaulders made from iron and leather.", kind: Armor( - kind: Shoulder(IronLeather2), - stats: (20), + ( + kind: Shoulder(IronLeather2), + stats: ( + protection: Normal(9.0), + ), + ) ), ) diff --git a/assets/common/items/armor/shoulder/leather_iron_3.ron b/assets/common/items/armor/shoulder/leather_iron_3.ron index 2c30a2452a..fb0eb45951 100644 --- a/assets/common/items/armor/shoulder/leather_iron_3.ron +++ b/assets/common/items/armor/shoulder/leather_iron_3.ron @@ -1,8 +1,12 @@ Item( name: "Iron and Leather Spaulders", - description: "Robust spaulders made from iron and leather.\n\n", + description: "Robust spaulders made from iron and leather.", kind: Armor( - kind: Shoulder(IronLeather3), - stats: (20), + ( + kind: Shoulder(IronLeather3), + stats: ( + protection: Normal(9.0), + ), + ) ), ) diff --git a/assets/common/items/armor/shoulder/leather_strips.ron b/assets/common/items/armor/shoulder/leather_strips.ron index e8a84127f8..fa0e176e2b 100644 --- a/assets/common/items/armor/shoulder/leather_strips.ron +++ b/assets/common/items/armor/shoulder/leather_strips.ron @@ -1,8 +1,12 @@ Item( name: "Leather Strips", - description: "Shoulder wraps made from leather strips.\n\n", + description: "Shoulder wraps made from leather strips.", kind: Armor( - kind: Shoulder(LeatherStrips), - stats: (20), + ( + kind: Shoulder(LeatherStrips), + stats: ( + protection: Normal(4.0), + ), + ) ), ) diff --git a/assets/common/items/armor/shoulder/plate_0.ron b/assets/common/items/armor/shoulder/plate_0.ron index f6d3cc8590..54c7fa4dbf 100644 --- a/assets/common/items/armor/shoulder/plate_0.ron +++ b/assets/common/items/armor/shoulder/plate_0.ron @@ -1,8 +1,12 @@ Item( name: "Iron Shoulderguards", - description: "Shoulders\n\nArmor: 0\n\nA strong shoulder to lean on.\n\n", + description: "Shoulderguards forged from iron.", kind: Armor( - kind: Shoulder(Plate0), - stats: (20), + ( + kind: Shoulder(Plate0), + stats: ( + protection: Normal(12.0), + ), + ) ), ) diff --git a/assets/common/items/armor/shoulder/steel_0.ron b/assets/common/items/armor/shoulder/steel_0.ron index 6c48846f9a..0ccd4a907a 100644 --- a/assets/common/items/armor/shoulder/steel_0.ron +++ b/assets/common/items/armor/shoulder/steel_0.ron @@ -1,8 +1,12 @@ Item( name: "Steel Shoulder Pad", - description: "A simple shoulder pad made of steel", + description: "A simple shoulder pad made of steel.", kind: Armor( - kind: Shoulder(Steel0), - stats: (20), + ( + kind: Shoulder(Steel0), + stats: ( + protection: Normal(15.0), + ), + ) ), ) diff --git a/assets/common/items/armor/shoulder/twigs.ron b/assets/common/items/armor/shoulder/twigs.ron index 144d5552b0..65995c6699 100644 --- a/assets/common/items/armor/shoulder/twigs.ron +++ b/assets/common/items/armor/shoulder/twigs.ron @@ -1,8 +1,12 @@ Item( name: "Twiggy Shoulders", - description: "Spaulders made from tightly tied twigs.\n\n", + description: "Spaulders made from tightly tied twigs.", kind: Armor( - kind: Shoulder(TwiggyShoulder), - stats: (20), + ( + kind: Shoulder(TwiggyShoulder), + stats: ( + protection: Normal(9.0), + ), + ) ), ) diff --git a/assets/common/items/armor/shoulder/twigsflowers.ron b/assets/common/items/armor/shoulder/twigsflowers.ron index d62e5d3e10..e8f2fecfe2 100644 --- a/assets/common/items/armor/shoulder/twigsflowers.ron +++ b/assets/common/items/armor/shoulder/twigsflowers.ron @@ -1,8 +1,12 @@ Item( name: "Flowery Shoulders", - description: "Flowery, leafy spaulders.\n\n", + description: "Flowery, leafy spaulders.", kind: Armor( - kind: Shoulder(FlowerShoulder), - stats: (20), + ( + kind: Shoulder(FlowerShoulder), + stats: ( + protection: Normal(9.0), + ), + ) ), ) diff --git a/assets/common/items/armor/shoulder/twigsleaves.ron b/assets/common/items/armor/shoulder/twigsleaves.ron index dc88f790e2..9e87f878f5 100644 --- a/assets/common/items/armor/shoulder/twigsleaves.ron +++ b/assets/common/items/armor/shoulder/twigsleaves.ron @@ -1,8 +1,12 @@ Item( name: "Leafy Shoulders", - description: "Spaulders made from tied twigs and leaves.\n\n", + description: "Spaulders made from tied twigs and leaves.", kind: Armor( - kind: Shoulder(LeafyShoulder), - stats: (20), + ( + kind: Shoulder(LeafyShoulder), + stats: ( + protection: Normal(9.0), + ), + ) ), ) diff --git a/assets/common/items/armor/starter/rugged_chest.ron b/assets/common/items/armor/starter/rugged_chest.ron index d559dd6dec..2ad170a8ae 100644 --- a/assets/common/items/armor/starter/rugged_chest.ron +++ b/assets/common/items/armor/starter/rugged_chest.ron @@ -1,8 +1,12 @@ Item( name: "Rugged Shirt", - description: "Chest\n\nArmor: 0\n\nSmells like Adventure.\n\n", + description: "Smells like Adventure.", kind: Armor( - kind: Chest(Rugged0), - stats: (20), + ( + kind: Chest(Rugged0), + stats: ( + protection: Normal(1.0), + ), + ) ), ) diff --git a/assets/common/items/armor/starter/rugged_pants.ron b/assets/common/items/armor/starter/rugged_pants.ron index 0444aeb665..3b46eb61ba 100644 --- a/assets/common/items/armor/starter/rugged_pants.ron +++ b/assets/common/items/armor/starter/rugged_pants.ron @@ -1,8 +1,12 @@ Item( name: "Rugged Commoner's Pants", - description: "Legs\n\nArmor: 0\n\nThey remind you of the old days.\n\n", + description: "They remind you of the old days.", kind: Armor( - kind: Pants(Rugged0), - stats: (20), + ( + kind: Pants(Rugged0), + stats: ( + protection: Normal(1.0), + ), + ) ), ) diff --git a/assets/common/items/armor/starter/sandals_0.ron b/assets/common/items/armor/starter/sandals_0.ron index 3d2201aa19..cbfd3b4805 100644 --- a/assets/common/items/armor/starter/sandals_0.ron +++ b/assets/common/items/armor/starter/sandals_0.ron @@ -1,8 +1,12 @@ Item( name: "Worn out Sandals", - description: "Feet\n\nArmor: 0\n\nLoyal companions.\n\n", + description: "Loyal companions.", kind: Armor( - kind: Foot(Sandal0), - stats: (20), + ( + kind: Foot(Sandal0), + stats: ( + protection: Normal(0.0), + ), + ) ), ) diff --git a/assets/common/items/armor/tabard/admin.ron b/assets/common/items/armor/tabard/admin.ron index 4ad17edb50..665541e195 100644 --- a/assets/common/items/armor/tabard/admin.ron +++ b/assets/common/items/armor/tabard/admin.ron @@ -1,8 +1,12 @@ Item( name: "Admin's Tabard", - description: "Tabard\n\nWith great power comes\ngreat responsibility.\n\n", + description: "With great power comes\ngreat responsibility.", kind: Armor( - kind: Tabard(Admin), - stats: (20), + ( + kind: Tabard(Admin), + stats: ( + protection: Normal(0.0), + ), + ) ), ) diff --git a/assets/common/items/debug/admin.ron b/assets/common/items/debug/admin.ron index d1741be6b5..433e9c0ecc 100644 --- a/assets/common/items/debug/admin.ron +++ b/assets/common/items/debug/admin.ron @@ -1,8 +1,12 @@ Item( name: "Admin's Tabard", - description: "Tabard\n\nWith great power comes\ngreat responsibility. ", + description: "With great power comes\ngreat responsibility.", kind: Armor( - kind: Tabard(Admin), - stats: (20), + ( + kind: Tabard(Admin), + stats: ( + protection: Invincible, + ), + ) ), ) diff --git a/assets/common/items/debug/admin_back.ron b/assets/common/items/debug/admin_back.ron index dbdfed10f3..d491343bbf 100644 --- a/assets/common/items/debug/admin_back.ron +++ b/assets/common/items/debug/admin_back.ron @@ -1,8 +1,12 @@ Item( name: "Admin's Cape", - description: "Back\n\nArmor: 0\n\nWith great power comes\ngreat responsibility. ", + description: "With great power comes\ngreat responsibility.", kind: Armor( - kind: Back(Admin), - stats: (20), + ( + kind: Back(Admin), + stats: ( + protection: Invincible, + ), + ) ), ) diff --git a/assets/common/items/debug/cultist_belt.ron b/assets/common/items/debug/cultist_belt.ron index 1b2765d2c8..648c2a5adb 100644 --- a/assets/common/items/debug/cultist_belt.ron +++ b/assets/common/items/debug/cultist_belt.ron @@ -1,8 +1,12 @@ Item( name: "Cultist Belt", - description: "Belt\n\nArmor: 0\n\n", + description: "Ceremonial attire used by members.", kind: Armor( - kind: Belt(Cultist), - stats: (20), + ( + kind: Belt(Cultist), + stats: ( + protection: Invincible, + ), + ) ), ) diff --git a/assets/common/items/debug/cultist_boots.ron b/assets/common/items/debug/cultist_boots.ron index 70d134e5fe..5a130430be 100644 --- a/assets/common/items/debug/cultist_boots.ron +++ b/assets/common/items/debug/cultist_boots.ron @@ -1,8 +1,12 @@ Item( name: "Cultist Boots", - description: "Feet\n\nArmor: 0\n\n", + description: "Ceremonial attire used by members.", kind: Armor( - kind: Foot(Cultist), - stats: (20), + ( + kind: Foot(Cultist), + stats: ( + protection: Invincible, + ), + ) ), ) diff --git a/assets/common/items/debug/cultist_chest_blue.ron b/assets/common/items/debug/cultist_chest_blue.ron index 2039ea6d76..7ae1a9f3ab 100644 --- a/assets/common/items/debug/cultist_chest_blue.ron +++ b/assets/common/items/debug/cultist_chest_blue.ron @@ -1,8 +1,12 @@ Item( name: "Blue Cultist Chest", - description: "Chest\n\nArmor: 0\n\n", + description: "Ceremonial attire used by members.", kind: Armor( - kind: Chest(CultistBlue), - stats: (20), + ( + kind: Chest(CultistBlue), + stats: ( + protection: Invincible, + ), + ) ), ) diff --git a/assets/common/items/debug/cultist_hands_blue.ron b/assets/common/items/debug/cultist_hands_blue.ron index c6d3076b58..e9955b2a23 100644 --- a/assets/common/items/debug/cultist_hands_blue.ron +++ b/assets/common/items/debug/cultist_hands_blue.ron @@ -1,8 +1,12 @@ Item( name: "Blue Cultist Gloves", - description: "Hands\n\nArmor: 0\n\n", + description: "Ceremonial attire used by members.", kind: Armor( - kind: Hand(CultistBlue), - stats: (20), + ( + kind: Hand(CultistBlue), + stats: ( + protection: Invincible, + ), + ) ), ) diff --git a/assets/common/items/debug/cultist_legs_blue.ron b/assets/common/items/debug/cultist_legs_blue.ron index 5ad8a8c25a..4ab7fa5119 100644 --- a/assets/common/items/debug/cultist_legs_blue.ron +++ b/assets/common/items/debug/cultist_legs_blue.ron @@ -1,8 +1,12 @@ Item( name: "Blue Cultist Skirt", - description: "Legs\n\nArmor: 0\n\n", + description: "Ceremonial attire used by members.", kind: Armor( - kind: Pants(CultistBlue), - stats: (20), + ( + kind: Pants(CultistBlue), + stats: ( + protection: Invincible, + ), + ) ), ) diff --git a/assets/common/items/debug/cultist_shoulder_blue.ron b/assets/common/items/debug/cultist_shoulder_blue.ron index 1e4d3965c8..92c1ff5092 100644 --- a/assets/common/items/debug/cultist_shoulder_blue.ron +++ b/assets/common/items/debug/cultist_shoulder_blue.ron @@ -1,8 +1,12 @@ Item( name: "Blue Cultist Mantle", - description: "Shoulders\n\nArmor: 0\n\nA strong shoulder to lean on.\n\n", + description: "Ceremonial attire used by members.", kind: Armor( - kind: Shoulder(CultistBlue), - stats: (20), + ( + kind: Shoulder(CultistBlue), + stats: ( + protection: Invincible, + ), + ) ), ) diff --git a/assets/common/items/testing/test_boots.ron b/assets/common/items/testing/test_boots.ron index b8a4bfa3cf..405cd4d7c9 100644 --- a/assets/common/items/testing/test_boots.ron +++ b/assets/common/items/testing/test_boots.ron @@ -2,7 +2,11 @@ Item( name: "Testing Boots", description: "Hopefully this test doesn't break!", kind: Armor( - kind: Foot(Dark), - stats: (20), + ( + kind: Foot(Dark), + stats: ( + protection: Normal(0.0), + ), + ) ), ) diff --git a/assets/common/loot_table.ron b/assets/common/loot_table.ron index 7358a55729..2ba9b1da1d 100644 --- a/assets/common/loot_table.ron +++ b/assets/common/loot_table.ron @@ -59,53 +59,110 @@ // bows (1, "common.items.weapons.bow.starter_bow"), // belts - (0.2, "common.items.armor.belt.cloth_blue_0"), - (0.2, "common.items.armor.belt.cloth_green_0"), - (0.2, "common.items.armor.belt.cloth_purple_0"), - (0.15, "common.items.armor.belt.leather_0"), - (0.15, "common.items.armor.belt.leather_2"), - (0.1, "common.items.armor.belt.steel_0"), - (0.05, "common.items.armor.belt.plate_0"), + (0.17, "common.items.armor.belt.cloth_blue_0"), + (0.17, "common.items.armor.belt.cloth_green_0"), + (0.17, "common.items.armor.belt.cloth_purple_0"), + (0.08, "common.items.armor.belt.cultist_belt"), + (0.08, "common.items.armor.belt.assassin"), + (0.08, "common.items.armor.belt.druid"), + (0.06, "common.items.armor.belt.leather_0"), + (0.06, "common.items.armor.belt.leather_2"), + (0.02, "common.items.armor.belt.twig"), + (0.02, "common.items.armor.belt.twigsflowers"), + (0.02, "common.items.armor.belt.twigsleaves"), + (0.03, "common.items.armor.belt.plate_0"), + (0.01, "common.items.armor.belt.steel_0"), // chests - (0.2, "common.items.armor.chest.cloth_blue_0"), - (0.2, "common.items.armor.chest.cloth_green_0"), - (0.2, "common.items.armor.chest.cloth_purple_0"), - (0.15, "common.items.armor.chest.leather_0"), - (0.15, "common.items.armor.chest.leather_2"), - (0.1, "common.items.armor.chest.steel_0"), - (0.05, "common.items.armor.chest.plate_green_0"), + (0.08, "common.items.armor.chest.cloth_blue_0"), + (0.08, "common.items.armor.chest.cloth_green_0"), + (0.08, "common.items.armor.chest.cloth_purple_0"), + (0.025, "common.items.armor.chest.worker_green_0"), + (0.025, "common.items.armor.chest.worker_green_1"), + (0.025, "common.items.armor.chest.worker_orange_0"), + (0.025, "common.items.armor.chest.worker_orange_1"), + (0.025, "common.items.armor.chest.worker_purple_0"), + (0.025, "common.items.armor.chest.worker_purple_1"), + (0.025, "common.items.armor.chest.worker_red_0"), + (0.025, "common.items.armor.chest.worker_red_1"), + (0.025, "common.items.armor.chest.worker_yellow_0"), + (0.025, "common.items.armor.chest.worker_yellow_1"), + (0.08, "common.items.armor.chest.cultist_chest_purple"), + (0.08, "common.items.armor.chest.assassin"), + (0.08, "common.items.armor.chest.druid"), + (0.06, "common.items.armor.chest.leather_0"), + (0.06, "common.items.armor.chest.leather_2"), + (0.02, "common.items.armor.chest.twig"), + (0.02, "common.items.armor.chest.twigsflowers"), + (0.02, "common.items.armor.chest.twigsleaves"), + (0.03, "common.items.armor.chest.plate_green_0"), + (0.01, "common.items.armor.chest.steel_0"), // shoes - (0.2, "common.items.armor.foot.cloth_blue_0"), - (0.2, "common.items.armor.foot.cloth_green_0"), - (0.2, "common.items.armor.foot.cloth_purple_0"), - (0.15, "common.items.armor.foot.leather_0"), - (0.15, "common.items.armor.foot.leather_2"), - (0.1, "common.items.armor.foot.steel_0"), - (0.05, "common.items.armor.foot.plate_0"), + (0.15, "common.items.armor.foot.cloth_blue_0"), + (0.15, "common.items.armor.foot.cloth_green_0"), + (0.15, "common.items.armor.foot.cloth_purple_0"), + (0.05, "common.items.armor.foot.jackalope_slippers"), + (0.08, "common.items.armor.foot.cultist_boots"), + (0.08, "common.items.armor.foot.assassin"), + (0.08, "common.items.armor.foot.druid"), + (0.06, "common.items.armor.foot.leather_0"), + (0.06, "common.items.armor.foot.leather_2"), + (0.02, "common.items.armor.foot.twig"), + (0.02, "common.items.armor.foot.twigsflowers"), + (0.02, "common.items.armor.foot.twigsleaves"), + (0.03, "common.items.armor.foot.plate_0"), + (0.01, "common.items.armor.foot.steel_0"), // pants - (0.2, "common.items.armor.pants.cloth_blue_0"), - (0.2, "common.items.armor.pants.cloth_green_0"), - (0.2, "common.items.armor.pants.cloth_purple_0"), - (0.15, "common.items.armor.pants.leather_0"), - (0.1, "common.items.armor.pants.steel_0"), - (0.05, "common.items.armor.pants.plate_green_0"), + (0.125, "common.items.armor.pants.cloth_blue_0"), + (0.125, "common.items.armor.pants.cloth_green_0"), + (0.125, "common.items.armor.pants.cloth_purple_0"), + (0.125, "common.items.armor.pants.worker_blue_0"), + (0.08, "common.items.armor.pants.cultist_legs_purple"), + (0.08, "common.items.armor.pants.assassin"), + (0.08, "common.items.armor.pants.druid"), + (0.04, "common.items.armor.pants.leather_0"), + (0.04, "common.items.armor.pants.leather_2"), + (0.04, "common.items.armor.pants.hunting"), + (0.02, "common.items.armor.pants.twig"), + (0.02, "common.items.armor.pants.twigsflowers"), + (0.02, "common.items.armor.pants.twigsleaves"), + (0.03, "common.items.armor.pants.plate_green_0"), + (0.01, "common.items.armor.pants.steel_0"), // shoulders - (0.2, "common.items.armor.shoulder.cloth_blue_0"), - (0.2, "common.items.armor.shoulder.cloth_green_0"), - (0.2, "common.items.armor.shoulder.cloth_purple_0"), - (0.1, "common.items.armor.shoulder.leather_0"), - (0.1, "common.items.armor.shoulder.leather_1"), - (0.1, "common.items.armor.shoulder.leather_2"), - (0.1, "common.items.armor.shoulder.steel_0"), - (0.05, "common.items.armor.shoulder.plate_0"), + (0.125, "common.items.armor.shoulder.cloth_blue_0"), + (0.125, "common.items.armor.shoulder.cloth_blue_1"), + (0.125, "common.items.armor.shoulder.cloth_green_0"), + (0.125, "common.items.armor.shoulder.cloth_purple_0"), + (0.06, "common.items.armor.shoulder.cultist_shoulder_purple"), + (0.06, "common.items.armor.shoulder.assassin"), + (0.06, "common.items.armor.shoulder.druidshoulder"), + (0.06, "common.items.armor.shoulder.leather_strips"), + (0.04, "common.items.armor.shoulder.leather_0"), + (0.04, "common.items.armor.shoulder.leather_1"), + (0.04, "common.items.armor.shoulder.leather_2"), + (0.01, "common.items.armor.shoulder.twigs"), + (0.01, "common.items.armor.shoulder.twigsflowers"), + (0.01, "common.items.armor.shoulder.twigsleaves"), + (0.01, "common.items.armor.shoulder.leather_iron_0"), + (0.01, "common.items.armor.shoulder.leather_iron_1"), + (0.01, "common.items.armor.shoulder.leather_iron_2"), + (0.01, "common.items.armor.shoulder.leather_iron_3"), + (0.015, "common.items.armor.shoulder.plate_0"), + (0.015, "common.items.armor.shoulder.iron_spikes"), + (0.01, "common.items.armor.shoulder.steel_0"), //gloves - (0.2, "common.items.armor.hand.cloth_blue_0"), - (0.2, "common.items.armor.hand.cloth_green_0"), - (0.2, "common.items.armor.hand.cloth_purple_0"), - (0.15, "common.items.armor.hand.leather_0"), - (0.15, "common.items.armor.hand.leather_2"), - (0.1, "common.items.armor.hand.steel_0"), - (0.05, "common.items.armor.hand.plate_0"), + (0.17, "common.items.armor.hand.cloth_blue_0"), + (0.17, "common.items.armor.hand.cloth_green_0"), + (0.17, "common.items.armor.hand.cloth_purple_0"), + (0.08, "common.items.armor.hand.cultist_hands_purple"), + (0.08, "common.items.armor.hand.assassin"), + (0.08, "common.items.armor.hand.druid"), + (0.06, "common.items.armor.hand.leather_0"), + (0.06, "common.items.armor.hand.leather_2"), + (0.02, "common.items.armor.hand.twig"), + (0.02, "common.items.armor.hand.twigsflowers"), + (0.02, "common.items.armor.hand.twigsleaves"), + (0.03, "common.items.armor.hand.plate_0"), + (0.01, "common.items.armor.hand.steel_0"), // rings (0.6, "common.items.armor.ring.ring_0"), // capes diff --git a/assets/voxygen/element/icons/protection.png b/assets/voxygen/element/icons/protection.png new file mode 100644 index 0000000000..06e02352b5 --- /dev/null +++ b/assets/voxygen/element/icons/protection.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6412a1b82a084f9a7828f185752c891d40c5ce8c7575f91edfc360af47cf710f +size 404 diff --git a/assets/voxygen/i18n/en.ron b/assets/voxygen/i18n/en.ron index d727a07c90..7b5f5b2c4e 100644 --- a/assets/voxygen/i18n/en.ron +++ b/assets/voxygen/i18n/en.ron @@ -414,6 +414,8 @@ magically infused items?"#, Fitness Willpower + +Protection "#, /// End character window section diff --git a/common/Cargo.toml b/common/Cargo.toml index eda6f2efcd..47dd092a67 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -8,6 +8,7 @@ edition = "2018" no-assets = [] [dependencies] +arraygen = "0.1.13" specs-idvs = { git = "https://gitlab.com/veloren/specs-idvs.git", branch = "specs-git" } specs = { git = "https://github.com/amethyst/specs.git", features = ["serde", "storage-event-control"], rev = "7a2e348ab2223818bad487695c66c43db88050a5" } diff --git a/common/src/comp/ability.rs b/common/src/comp/ability.rs index b09b7bfaf8..b795222abe 100644 --- a/common/src/comp/ability.rs +++ b/common/src/comp/ability.rs @@ -1,11 +1,13 @@ use crate::{ comp::{ - ability::Stage, item::Item, Body, CharacterState, EnergySource, Gravity, LightEmitter, - Projectile, StateUpdate, + ability::Stage, + item::{armor::Protection, Item, ItemKind}, + Body, CharacterState, EnergySource, Gravity, LightEmitter, Projectile, StateUpdate, }, states::{triple_strike::*, *}, sys::character_behavior::JoinData, }; +use arraygen::Arraygen; use serde::{Deserialize, Serialize}; use specs::{Component, FlaggedStorage}; use specs_idvs::IdvStorage; @@ -144,25 +146,63 @@ pub struct ItemConfig { pub dodge_ability: Option, } -#[derive(Clone, PartialEq, Default, Debug, Serialize, Deserialize)] +#[derive(Arraygen, Clone, PartialEq, Default, Debug, Serialize, Deserialize)] +#[gen_array(pub fn get_armor: &Option)] pub struct Loadout { pub active_item: Option, pub second_item: Option, - pub shoulder: Option, - pub chest: Option, - pub belt: Option, - pub hand: Option, - pub pants: Option, - pub foot: Option, - pub back: Option, - pub ring: Option, - pub neck: Option, pub lantern: Option, + + #[in_array(get_armor)] + pub shoulder: Option, + #[in_array(get_armor)] + pub chest: Option, + #[in_array(get_armor)] + pub belt: Option, + #[in_array(get_armor)] + pub hand: Option, + #[in_array(get_armor)] + pub pants: Option, + #[in_array(get_armor)] + pub foot: Option, + #[in_array(get_armor)] + pub back: Option, + #[in_array(get_armor)] + pub ring: Option, + #[in_array(get_armor)] + pub neck: Option, + #[in_array(get_armor)] pub head: Option, + #[in_array(get_armor)] pub tabard: Option, } +impl Loadout { + pub fn get_damage_reduction(&self) -> f32 { + let protection = self + .get_armor() + .iter() + .flat_map(|armor| armor.as_ref()) + .filter_map(|item| { + if let ItemKind::Armor(armor) = item.kind { + Some(armor.get_protection()) + } else { + None + } + }) + .map(|protection| match protection { + Protection::Normal(protection) => Some(protection), + Protection::Invincible => None, + }) + .sum::>(); + match protection { + Some(dr) => dr / (60.0 + dr.abs()), + None => 1.0, + } + } +} + impl From<&CharacterAbility> for CharacterState { fn from(ability: &CharacterAbility) -> Self { match ability { diff --git a/common/src/comp/body.rs b/common/src/comp/body.rs index 8f1b7482a0..80eaef09f8 100644 --- a/common/src/comp/body.rs +++ b/common/src/comp/body.rs @@ -130,6 +130,114 @@ impl Body { // Note: currently assumes sphericality pub fn height(&self) -> f32 { self.radius() * 2.0 } + + pub fn base_health(&self) -> u32 { + match self { + Body::Humanoid(_) => 52, + Body::QuadrupedSmall(_) => 44, + Body::QuadrupedMedium(_) => 72, + Body::BirdMedium(_) => 36, + Body::FishMedium(_) => 32, + Body::Dragon(_) => 256, + Body::BirdSmall(_) => 24, + Body::FishSmall(_) => 20, + Body::BipedLarge(_) => 144, + Body::Object(_) => 100, + Body::Golem(_) => 168, + Body::Critter(_) => 32, + Body::QuadrupedLow(_) => 64, + } + } + + pub fn base_health_increase(&self) -> u32 { + match self { + Body::Humanoid(_) => 5, + Body::QuadrupedSmall(_) => 4, + Body::QuadrupedMedium(_) => 7, + Body::BirdMedium(_) => 4, + Body::FishMedium(_) => 3, + Body::Dragon(_) => 26, + Body::BirdSmall(_) => 2, + Body::FishSmall(_) => 2, + Body::BipedLarge(_) => 14, + Body::Object(_) => 0, + Body::Golem(_) => 17, + Body::Critter(_) => 3, + Body::QuadrupedLow(_) => 6, + } + } + + pub fn base_exp(&self) -> u32 { + match self { + Body::Humanoid(_) => 15, + Body::QuadrupedSmall(_) => 12, + Body::QuadrupedMedium(_) => 28, + Body::BirdMedium(_) => 10, + Body::FishMedium(_) => 8, + Body::Dragon(_) => 160, + Body::BirdSmall(_) => 5, + Body::FishSmall(_) => 4, + Body::BipedLarge(_) => 75, + Body::Object(_) => 0, + Body::Golem(_) => 75, + Body::Critter(_) => 8, + Body::QuadrupedLow(_) => 24, + } + } + + pub fn base_exp_increase(&self) -> u32 { + match self { + Body::Humanoid(_) => 3, + Body::QuadrupedSmall(_) => 2, + Body::QuadrupedMedium(_) => 6, + Body::BirdMedium(_) => 2, + Body::FishMedium(_) => 2, + Body::Dragon(_) => 32, + Body::BirdSmall(_) => 1, + Body::FishSmall(_) => 1, + Body::BipedLarge(_) => 15, + Body::Object(_) => 0, + Body::Golem(_) => 15, + Body::Critter(_) => 2, + Body::QuadrupedLow(_) => 5, + } + } + + pub fn base_dmg(&self) -> u32 { + match self { + Body::Humanoid(_) => 6, + Body::QuadrupedSmall(_) => 8, + Body::QuadrupedMedium(_) => 12, + Body::BirdMedium(_) => 7, + Body::FishMedium(_) => 6, + Body::Dragon(_) => 90, + Body::BirdSmall(_) => 5, + Body::FishSmall(_) => 3, + Body::BipedLarge(_) => 36, + Body::Object(_) => 0, + Body::Golem(_) => 36, + Body::Critter(_) => 7, + Body::QuadrupedLow(_) => 11, + } + } + + pub fn base_range(&self) -> f32 { + match self { + Body::Humanoid(_) => 5.0, + Body::QuadrupedSmall(_) => 4.5, + Body::QuadrupedMedium(_) => 5.5, + Body::BirdMedium(_) => 3.5, + Body::FishMedium(_) => 3.5, + Body::Dragon(_) => 12.5, + Body::BirdSmall(_) => 3.0, + Body::FishSmall(_) => 3.0, + Body::BipedLarge(_) => 10.0, + Body::Object(_) => 3.0, + Body::Golem(_) => 7.5, + Body::Critter(_) => 3.0, + Body::QuadrupedLow(_) => 4.5, + } + } } impl Component for Body { diff --git a/common/src/comp/inventory/item/armor.rs b/common/src/comp/inventory/item/armor.rs index 65e1ebe560..f9df886468 100644 --- a/common/src/comp/inventory/item/armor.rs +++ b/common/src/comp/inventory/item/armor.rs @@ -335,7 +335,7 @@ pub enum Tabard { pub const ALL_TABARDS: [Tabard; 1] = [Tabard::Admin]; #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] -pub enum Armor { +pub enum ArmorKind { Shoulder(Shoulder), Chest(Chest), Belt(Belt), @@ -354,9 +354,27 @@ impl Armor { /// one another (i.e: one may be substituted for the other in crafting /// recipes or item possession checks). pub fn superficially_eq(&self, other: &Self) -> bool { - std::mem::discriminant(self) == std::mem::discriminant(other) + std::mem::discriminant(&self.kind) == std::mem::discriminant(&other.kind) } } -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] -pub struct Stats(pub u32); +#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] +pub struct Stats { + protection: Protection, +} + +#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] +pub enum Protection { + Invincible, + Normal(f32), +} + +#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] +pub struct Armor { + pub kind: ArmorKind, + pub stats: Stats, +} + +impl Armor { + pub fn get_protection(&self) -> Protection { self.stats.protection } +} diff --git a/common/src/comp/inventory/item/mod.rs b/common/src/comp/inventory/item/mod.rs index a210ae2d8b..895f07bd34 100644 --- a/common/src/comp/inventory/item/mod.rs +++ b/common/src/comp/inventory/item/mod.rs @@ -90,15 +90,12 @@ impl Lantern { fn default_amount() -> u32 { 1 } -#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub enum ItemKind { /// Something wieldable Tool(tool::Tool), Lantern(Lantern), - Armor { - kind: armor::Armor, - stats: armor::Stats, - }, + Armor(armor::Armor), Consumable { kind: Consumable, effect: Effect, @@ -122,7 +119,7 @@ pub enum ItemKind { }, } -#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct Item { name: String, description: String, @@ -237,9 +234,7 @@ impl Item { (ItemKind::Tool(a), ItemKind::Tool(b)) => a.superficially_eq(b), // TODO: Differentiate between lantern colors? (ItemKind::Lantern(_), ItemKind::Lantern(_)) => true, - (ItemKind::Armor { kind: a, .. }, ItemKind::Armor { kind: b, .. }) => { - a.superficially_eq(b) - }, + (ItemKind::Armor(a), ItemKind::Armor(b)) => a.superficially_eq(b), (ItemKind::Consumable { kind: a, .. }, ItemKind::Consumable { kind: b, .. }) => a == b, (ItemKind::Throwable { kind: a, .. }, ItemKind::Throwable { kind: b, .. }) => a == b, (ItemKind::Utility { kind: a, .. }, ItemKind::Utility { kind: b, .. }) => a == b, @@ -253,7 +248,7 @@ impl Component for Item { type Storage = FlaggedStorage>; } -#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct ItemDrop(pub Item); impl Component for ItemDrop { diff --git a/common/src/comp/inventory/mod.rs b/common/src/comp/inventory/mod.rs index 22b3622971..875fab148c 100644 --- a/common/src/comp/inventory/mod.rs +++ b/common/src/comp/inventory/mod.rs @@ -11,7 +11,7 @@ use std::ops::Not; // The limit on distance between the entity and a collectible (squared) pub const MAX_PICKUP_RANGE_SQR: f32 = 64.0; -#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct Inventory { pub slots: Vec>, pub amount: u32, @@ -401,7 +401,7 @@ impl Component for Inventory { type Storage = HashMapStorage; } -#[derive(Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub enum InventoryUpdateEvent { Init, Used, diff --git a/common/src/comp/inventory/slot.rs b/common/src/comp/inventory/slot.rs index 312a81ac3c..db09354b25 100644 --- a/common/src/comp/inventory/slot.rs +++ b/common/src/comp/inventory/slot.rs @@ -1,4 +1,7 @@ -use crate::{comp, comp::item}; +use crate::{ + comp, + comp::{item, item::armor}, +}; use comp::{Inventory, Loadout}; use serde::{Deserialize, Serialize}; use tracing::warn; @@ -47,9 +50,10 @@ impl Slot { impl EquipSlot { fn can_hold(self, item_kind: &item::ItemKind) -> bool { + use armor::Armor; use item::ItemKind; match (self, item_kind) { - (Self::Armor(slot), ItemKind::Armor { kind, .. }) => slot.can_hold(kind), + (Self::Armor(slot), ItemKind::Armor(Armor { kind, .. })) => slot.can_hold(kind), (Self::Mainhand, ItemKind::Tool(_)) => true, (Self::Offhand, ItemKind::Tool(_)) => true, (Self::Lantern, ItemKind::Lantern(_)) => true, @@ -59,20 +63,20 @@ impl EquipSlot { } impl ArmorSlot { - fn can_hold(self, armor: &item::armor::Armor) -> bool { - use item::armor::Armor; + fn can_hold(self, armor: &item::armor::ArmorKind) -> bool { + use item::armor::ArmorKind; match (self, armor) { - (Self::Head, Armor::Head(_)) => true, - (Self::Neck, Armor::Neck(_)) => true, - (Self::Shoulders, Armor::Shoulder(_)) => true, - (Self::Chest, Armor::Chest(_)) => true, - (Self::Hands, Armor::Hand(_)) => true, - (Self::Ring, Armor::Ring(_)) => true, - (Self::Back, Armor::Back(_)) => true, - (Self::Belt, Armor::Belt(_)) => true, - (Self::Legs, Armor::Pants(_)) => true, - (Self::Feet, Armor::Foot(_)) => true, - (Self::Tabard, Armor::Tabard(_)) => true, + (Self::Head, ArmorKind::Head(_)) => true, + (Self::Neck, ArmorKind::Neck(_)) => true, + (Self::Shoulders, ArmorKind::Shoulder(_)) => true, + (Self::Chest, ArmorKind::Chest(_)) => true, + (Self::Hands, ArmorKind::Hand(_)) => true, + (Self::Ring, ArmorKind::Ring(_)) => true, + (Self::Back, ArmorKind::Back(_)) => true, + (Self::Belt, ArmorKind::Belt(_)) => true, + (Self::Legs, ArmorKind::Pants(_)) => true, + (Self::Feet, ArmorKind::Foot(_)) => true, + (Self::Tabard, ArmorKind::Tabard(_)) => true, _ => false, } } @@ -285,22 +289,23 @@ pub fn swap( /// assert_eq!(boots, loadout.foot); /// ``` pub fn equip(slot: usize, inventory: &mut Inventory, loadout: &mut Loadout) { - use item::{armor::Armor, ItemKind}; + use armor::Armor; + use item::{armor::ArmorKind, ItemKind}; let equip_slot = inventory.get(slot).and_then(|i| match &i.kind { ItemKind::Tool(_) => Some(EquipSlot::Mainhand), - ItemKind::Armor { kind, .. } => Some(EquipSlot::Armor(match kind { - Armor::Head(_) => ArmorSlot::Head, - Armor::Neck(_) => ArmorSlot::Neck, - Armor::Shoulder(_) => ArmorSlot::Shoulders, - Armor::Chest(_) => ArmorSlot::Chest, - Armor::Hand(_) => ArmorSlot::Hands, - Armor::Ring(_) => ArmorSlot::Ring, - Armor::Back(_) => ArmorSlot::Back, - Armor::Belt(_) => ArmorSlot::Belt, - Armor::Pants(_) => ArmorSlot::Legs, - Armor::Foot(_) => ArmorSlot::Feet, - Armor::Tabard(_) => ArmorSlot::Tabard, + ItemKind::Armor(Armor { kind, .. }) => Some(EquipSlot::Armor(match kind { + ArmorKind::Head(_) => ArmorSlot::Head, + ArmorKind::Neck(_) => ArmorSlot::Neck, + ArmorKind::Shoulder(_) => ArmorSlot::Shoulders, + ArmorKind::Chest(_) => ArmorSlot::Chest, + ArmorKind::Hand(_) => ArmorSlot::Hands, + ArmorKind::Ring(_) => ArmorSlot::Ring, + ArmorKind::Back(_) => ArmorSlot::Back, + ArmorKind::Belt(_) => ArmorSlot::Belt, + ArmorKind::Pants(_) => ArmorSlot::Legs, + ArmorKind::Foot(_) => ArmorSlot::Feet, + ArmorKind::Tabard(_) => ArmorSlot::Tabard, })), ItemKind::Lantern(_) => Some(EquipSlot::Lantern), _ => None, diff --git a/common/src/comp/stats.rs b/common/src/comp/stats.rs index 98392efbd9..3e8bd44a9e 100644 --- a/common/src/comp/stats.rs +++ b/common/src/comp/stats.rs @@ -134,80 +134,6 @@ pub struct Stats { pub body_type: Body, } -impl Body { - pub fn base_health(&self) -> u32 { - match self { - Body::Humanoid(_) => 52, - Body::QuadrupedSmall(_) => 44, - Body::QuadrupedMedium(_) => 72, - Body::BirdMedium(_) => 36, - Body::FishMedium(_) => 32, - Body::Dragon(_) => 256, - Body::BirdSmall(_) => 24, - Body::FishSmall(_) => 20, - Body::BipedLarge(_) => 144, - Body::Object(_) => 100, - Body::Golem(_) => 168, - Body::Critter(_) => 32, - Body::QuadrupedLow(_) => 64, - } - } - - pub fn base_health_increase(&self) -> u32 { - match self { - Body::Humanoid(_) => 5, - Body::QuadrupedSmall(_) => 4, - Body::QuadrupedMedium(_) => 7, - Body::BirdMedium(_) => 4, - Body::FishMedium(_) => 3, - Body::Dragon(_) => 26, - Body::BirdSmall(_) => 2, - Body::FishSmall(_) => 2, - Body::BipedLarge(_) => 14, - Body::Object(_) => 0, - Body::Golem(_) => 17, - Body::Critter(_) => 3, - Body::QuadrupedLow(_) => 6, - } - } - - pub fn base_exp(&self) -> u32 { - match self { - Body::Humanoid(_) => 15, - Body::QuadrupedSmall(_) => 12, - Body::QuadrupedMedium(_) => 28, - Body::BirdMedium(_) => 10, - Body::FishMedium(_) => 8, - Body::Dragon(_) => 160, - Body::BirdSmall(_) => 5, - Body::FishSmall(_) => 4, - Body::BipedLarge(_) => 75, - Body::Object(_) => 0, - Body::Golem(_) => 75, - Body::Critter(_) => 8, - Body::QuadrupedLow(_) => 24, - } - } - - pub fn base_exp_increase(&self) -> u32 { - match self { - Body::Humanoid(_) => 3, - Body::QuadrupedSmall(_) => 2, - Body::QuadrupedMedium(_) => 6, - Body::BirdMedium(_) => 2, - Body::FishMedium(_) => 2, - Body::Dragon(_) => 32, - Body::BirdSmall(_) => 1, - Body::FishSmall(_) => 1, - Body::BipedLarge(_) => 15, - Body::Object(_) => 0, - Body::Golem(_) => 15, - Body::Critter(_) => 2, - Body::QuadrupedLow(_) => 5, - } - } -} - impl Stats { pub fn should_die(&self) -> bool { self.health.current == 0 } diff --git a/common/src/loadout_builder.rs b/common/src/loadout_builder.rs index 1ed2512b0a..b3b65bd5bd 100644 --- a/common/src/loadout_builder.rs +++ b/common/src/loadout_builder.rs @@ -24,44 +24,6 @@ use std::time::Duration; /// ``` pub struct LoadoutBuilder(Loadout); -impl Body { - pub fn base_dmg(&self) -> u32 { - match self { - Body::Humanoid(_) => 8, - Body::QuadrupedSmall(_) => 7, - Body::QuadrupedMedium(_) => 10, - Body::BirdMedium(_) => 6, - Body::FishMedium(_) => 5, - Body::Dragon(_) => 75, - Body::BirdSmall(_) => 4, - Body::FishSmall(_) => 3, - Body::BipedLarge(_) => 30, - Body::Object(_) => 0, - Body::Golem(_) => 30, - Body::Critter(_) => 6, - Body::QuadrupedLow(_) => 9, - } - } - - pub fn base_range(&self) -> f32 { - match self { - Body::Humanoid(_) => 5.0, - Body::QuadrupedSmall(_) => 4.5, - Body::QuadrupedMedium(_) => 5.5, - Body::BirdMedium(_) => 3.5, - Body::FishMedium(_) => 3.5, - Body::Dragon(_) => 12.5, - Body::BirdSmall(_) => 3.0, - Body::FishSmall(_) => 3.0, - Body::BipedLarge(_) => 10.0, - Body::Object(_) => 3.0, - Body::Golem(_) => 7.5, - Body::Critter(_) => 3.0, - Body::QuadrupedLow(_) => 4.5, - } - } -} - impl LoadoutBuilder { #[allow(clippy::new_without_default)] // TODO: Pending review in #587 pub fn new() -> Self { diff --git a/common/src/sys/combat.rs b/common/src/sys/combat.rs index f0e84e65bb..f11714b23b 100644 --- a/common/src/sys/combat.rs +++ b/common/src/sys/combat.rs @@ -1,7 +1,7 @@ use crate::{ comp::{ - Alignment, Attacking, Body, CharacterState, HealthChange, HealthSource, Ori, Pos, Scale, - Stats, + Alignment, Attacking, Body, CharacterState, HealthChange, HealthSource, Loadout, Ori, Pos, + Scale, Stats, }, event::{EventBus, LocalEvent, ServerEvent}, sync::Uid, @@ -29,6 +29,7 @@ impl<'a> System<'a> for Sys { ReadStorage<'a, Alignment>, ReadStorage<'a, Body>, ReadStorage<'a, Stats>, + ReadStorage<'a, Loadout>, WriteStorage<'a, Attacking>, WriteStorage<'a, CharacterState>, ); @@ -46,6 +47,7 @@ impl<'a> System<'a> for Sys { alignments, bodies, stats, + loadouts, mut attacking_storage, character_states, ): Self::SystemData, @@ -145,6 +147,12 @@ impl<'a> System<'a> for Sys { healthchange *= 1.0 - BLOCK_EFFICIENCY } + // Armor + if let Some(loadout) = loadouts.get(b) { + let damage_reduction = loadout.get_damage_reduction(); + healthchange *= 1.0 - damage_reduction; + } + if healthchange != 0.0 { server_emitter.emit(ServerEvent::Damage { uid: *uid_b, diff --git a/server/src/migrations/2020-07-16-044718_migrate_armour_stats/down.sql b/server/src/migrations/2020-07-16-044718_migrate_armour_stats/down.sql new file mode 100644 index 0000000000..c1733c05f6 --- /dev/null +++ b/server/src/migrations/2020-07-16-044718_migrate_armour_stats/down.sql @@ -0,0 +1 @@ +-- No down action for this migration \ No newline at end of file diff --git a/server/src/migrations/2020-07-16-044718_migrate_armour_stats/up.sql b/server/src/migrations/2020-07-16-044718_migrate_armour_stats/up.sql new file mode 100644 index 0000000000..b55f5eec50 --- /dev/null +++ b/server/src/migrations/2020-07-16-044718_migrate_armour_stats/up.sql @@ -0,0 +1,29 @@ +-- This migration updates all "stats" fields for each armour item in player loadouts +UPDATE + loadout +SET + items = json_replace( + items, + '$.back.kind.Armor.stats', + json('{ "protection": { "Normal": 1.0 } }'), + '$.belt.kind.Armor.stats', + json('{ "protection": { "Normal": 1.0 } }'), + '$.chest.kind.Armor.stats', + json('{ "protection": { "Normal": 1.0 } }'), + '$.foot.kind.Armor.stats', + json('{ "protection": { "Normal": 1.0 } }'), + '$.hand.kind.Armor.stats', + json('{ "protection": { "Normal": 1.0 } }'), + '$.head.kind.Armor.stats', + json('{ "protection": { "Normal": 1.0 } }'), + '$.neck.kind.Armor.stats', + json('{ "protection": { "Normal": 1.0 } }'), + '$.pants.kind.Armor.stats', + json('{ "protection": { "Normal": 1.0 } }'), + '$.ring.kind.Armor.stats', + json('{ "protection": { "Normal": 1.0 } }'), + '$.shoulder.kind.Armor.stats', + json('{ "protection": { "Normal": 1.0 } }'), + '$.tabard.kind.Armor.stats', + json('{ "protection": { "Normal": 1.0 } }') + ); \ No newline at end of file diff --git a/voxygen/src/hud/bag.rs b/voxygen/src/hud/bag.rs index 65f6e34d6d..0af0b8b244 100644 --- a/voxygen/src/hud/bag.rs +++ b/voxygen/src/hud/bag.rs @@ -2,6 +2,7 @@ use super::{ img_ids::{Imgs, ImgsRot}, item_imgs::ItemImgs, slots::{ArmorSlot, EquipSlot, InventorySlot, SlotManager}, + util::loadout_slot_text, Show, CRITICAL_HP_COLOR, LOW_HP_COLOR, TEXT_COLOR, UI_HIGHLIGHT_0, UI_MAIN, XP_COLOR, }; use crate::{ @@ -19,6 +20,7 @@ use conrod_core::{ widget::{self, Button, Image, Rectangle, Text}, widget_ids, Color, Colorable, Labelable, Positionable, Sizeable, Widget, WidgetCommon, }; + use vek::Vec2; widget_ids! { @@ -76,6 +78,7 @@ widget_ids! { end_ico, fit_ico, wp_ico, + prot_ico, } } @@ -339,180 +342,139 @@ impl<'a> Widget for Bag<'a> { image_source: self.item_imgs, slot_manager: Some(self.slot_manager), }; + let i18n = &self.localized_strings; // Head - let (title, desc) = loadout - .head - .as_ref() - .map_or((self.localized_strings.get("hud.bag.head"), ""), |item| { - (item.name(), item.description()) - }); + let (title, desc) = + loadout_slot_text(loadout.head.as_ref(), || (i18n.get("hud.bag.head"), "")); slot_maker .fabricate(EquipSlot::Armor(ArmorSlot::Head), [45.0; 2]) .mid_top_with_margin_on(state.ids.bg_frame, 60.0) .with_icon(self.imgs.head_bg, Vec2::new(32.0, 40.0), Some(UI_MAIN)) - .with_tooltip(self.tooltip_manager, title, desc, &item_tooltip) + .with_tooltip(self.tooltip_manager, title, &*desc, &item_tooltip) .set(state.ids.head_slot, ui); // Necklace - let (title, desc) = loadout - .neck - .as_ref() - .map_or((self.localized_strings.get("hud.bag.neck"), ""), |item| { - (item.name(), item.description()) - }); + let (title, desc) = + loadout_slot_text(loadout.neck.as_ref(), || (i18n.get("hud.bag.neck"), "")); slot_maker .fabricate(EquipSlot::Armor(ArmorSlot::Neck), [45.0; 2]) .mid_bottom_with_margin_on(state.ids.head_slot, -55.0) .with_icon(self.imgs.necklace_bg, Vec2::new(40.0, 31.0), Some(UI_MAIN)) - .with_tooltip(self.tooltip_manager, title, desc, &item_tooltip) + .with_tooltip(self.tooltip_manager, title, &*desc, &item_tooltip) .set(state.ids.neck_slot, ui); // Chest //Image::new(self.imgs.armor_slot) // different graphics for empty/non empty - let (title, desc) = loadout - .chest - .as_ref() - .map_or((self.localized_strings.get("hud.bag.chest"), ""), |item| { - (item.name(), item.description()) - }); + let (title, desc) = + loadout_slot_text(loadout.chest.as_ref(), || (i18n.get("hud.bag.chest"), "")); slot_maker .fabricate(EquipSlot::Armor(ArmorSlot::Chest), [85.0; 2]) .mid_bottom_with_margin_on(state.ids.neck_slot, -95.0) .with_icon(self.imgs.chest_bg, Vec2::new(64.0, 42.0), Some(UI_MAIN)) - .with_tooltip(self.tooltip_manager, title, desc, &item_tooltip) + .with_tooltip(self.tooltip_manager, title, &*desc, &item_tooltip) .set(state.ids.chest_slot, ui); // Shoulders - let (title, desc) = loadout.shoulder.as_ref().map_or( - (self.localized_strings.get("hud.bag.shoulders"), ""), - |item| (item.name(), item.description()), - ); + let (title, desc) = loadout_slot_text(loadout.shoulder.as_ref(), || { + (i18n.get("hud.bag.shoulders"), "") + }); slot_maker .fabricate(EquipSlot::Armor(ArmorSlot::Shoulders), [70.0; 2]) .bottom_left_with_margins_on(state.ids.chest_slot, 0.0, -80.0) .with_icon(self.imgs.shoulders_bg, Vec2::new(60.0, 36.0), Some(UI_MAIN)) - .with_tooltip(self.tooltip_manager, title, desc, &item_tooltip) + .with_tooltip(self.tooltip_manager, title, &*desc, &item_tooltip) .set(state.ids.shoulders_slot, ui); // Hands - let (title, desc) = loadout - .hand - .as_ref() - .map_or((self.localized_strings.get("hud.bag.hands"), ""), |item| { - (item.name(), item.description()) - }); + let (title, desc) = + loadout_slot_text(loadout.hand.as_ref(), || (i18n.get("hud.bag.hands"), "")); slot_maker .fabricate(EquipSlot::Armor(ArmorSlot::Hands), [70.0; 2]) .bottom_right_with_margins_on(state.ids.chest_slot, 0.0, -80.0) .with_icon(self.imgs.hands_bg, Vec2::new(55.0, 60.0), Some(UI_MAIN)) - .with_tooltip(self.tooltip_manager, title, desc, &item_tooltip) + .with_tooltip(self.tooltip_manager, title, &*desc, &item_tooltip) .set(state.ids.hands_slot, ui); // Belt - let (title, desc) = loadout - .belt - .as_ref() - .map_or((self.localized_strings.get("hud.bag.belt"), ""), |item| { - (item.name(), item.description()) - }); + let (title, desc) = + loadout_slot_text(loadout.belt.as_ref(), || (i18n.get("hud.bag.belt"), "")); slot_maker .fabricate(EquipSlot::Armor(ArmorSlot::Belt), [45.0; 2]) .mid_bottom_with_margin_on(state.ids.chest_slot, -55.0) .with_icon(self.imgs.belt_bg, Vec2::new(40.0, 23.0), Some(UI_MAIN)) - .with_tooltip(self.tooltip_manager, title, desc, &item_tooltip) + .with_tooltip(self.tooltip_manager, title, &*desc, &item_tooltip) .set(state.ids.belt_slot, ui); // Legs - let (title, desc) = loadout - .pants - .as_ref() - .map_or((self.localized_strings.get("hud.bag.legs"), ""), |item| { - (item.name(), item.description()) - }); + let (title, desc) = + loadout_slot_text(loadout.pants.as_ref(), || (i18n.get("hud.bag.legs"), "")); slot_maker .fabricate(EquipSlot::Armor(ArmorSlot::Legs), [85.0; 2]) .mid_bottom_with_margin_on(state.ids.belt_slot, -95.0) .with_icon(self.imgs.legs_bg, Vec2::new(48.0, 70.0), Some(UI_MAIN)) - .with_tooltip(self.tooltip_manager, title, desc, &item_tooltip) + .with_tooltip(self.tooltip_manager, title, &*desc, &item_tooltip) .set(state.ids.legs_slot, ui); // Lantern - let (title, desc) = loadout.lantern.as_ref().map_or( - (self.localized_strings.get("hud.bag.lantern"), ""), - |item| (item.name(), item.description()), - ); + let (title, desc) = loadout_slot_text(loadout.lantern.as_ref(), || { + (i18n.get("hud.bag.lantern"), "") + }); slot_maker .fabricate(EquipSlot::Lantern, [45.0; 2]) .bottom_right_with_margins_on(state.ids.shoulders_slot, -55.0, 0.0) .with_icon(self.imgs.lantern_bg, Vec2::new(24.0, 38.0), Some(UI_MAIN)) - .with_tooltip(self.tooltip_manager, title, desc, &item_tooltip) + .with_tooltip(self.tooltip_manager, title, &*desc, &item_tooltip) .set(state.ids.lantern_slot, ui); // Ring - let (title, desc) = loadout - .ring - .as_ref() - .map_or((self.localized_strings.get("hud.bag.ring"), ""), |item| { - (item.name(), item.description()) - }); + let (title, desc) = + loadout_slot_text(loadout.ring.as_ref(), || (i18n.get("hud.bag.ring"), "")); slot_maker .fabricate(EquipSlot::Armor(ArmorSlot::Ring), [45.0; 2]) .bottom_left_with_margins_on(state.ids.hands_slot, -55.0, 0.0) .with_icon(self.imgs.ring_bg, Vec2::new(36.0, 40.0), Some(UI_MAIN)) - .with_tooltip(self.tooltip_manager, title, desc, &item_tooltip) + .with_tooltip(self.tooltip_manager, title, &*desc, &item_tooltip) .set(state.ids.ring_slot, ui); // Back - let (title, desc) = loadout - .back - .as_ref() - .map_or((self.localized_strings.get("hud.bag.back"), ""), |item| { - (item.name(), item.description()) - }); + let (title, desc) = + loadout_slot_text(loadout.back.as_ref(), || (i18n.get("hud.bag.back"), "")); slot_maker .fabricate(EquipSlot::Armor(ArmorSlot::Back), [45.0; 2]) .down_from(state.ids.lantern_slot, 10.0) .with_icon(self.imgs.back_bg, Vec2::new(33.0, 40.0), Some(UI_MAIN)) - .with_tooltip(self.tooltip_manager, title, desc, &item_tooltip) + .with_tooltip(self.tooltip_manager, title, &*desc, &item_tooltip) .set(state.ids.back_slot, ui); // Foot - let (title, desc) = loadout - .foot - .as_ref() - .map_or((self.localized_strings.get("hud.bag.feet"), ""), |item| { - (item.name(), item.description()) - }); + let (title, desc) = + loadout_slot_text(loadout.foot.as_ref(), || (i18n.get("hud.bag.feet"), "")); slot_maker .fabricate(EquipSlot::Armor(ArmorSlot::Feet), [45.0; 2]) .down_from(state.ids.ring_slot, 10.0) .with_icon(self.imgs.feet_bg, Vec2::new(32.0, 40.0), Some(UI_MAIN)) - .with_tooltip(self.tooltip_manager, title, desc, &item_tooltip) + .with_tooltip(self.tooltip_manager, title, &*desc, &item_tooltip) .set(state.ids.feet_slot, ui); // Tabard - let (title, desc) = loadout - .tabard - .as_ref() - .map_or((self.localized_strings.get("hud.bag.tabard"), ""), |item| { - (item.name(), item.description()) - }); + let (title, desc) = + loadout_slot_text(loadout.tabard.as_ref(), || (i18n.get("hud.bag.tabard"), "")); slot_maker .fabricate(EquipSlot::Armor(ArmorSlot::Tabard), [70.0; 2]) .top_right_with_margins_on(state.ids.bg_frame, 80.5, 53.0) .with_icon(self.imgs.tabard_bg, Vec2::new(60.0, 60.0), Some(UI_MAIN)) - .with_tooltip(self.tooltip_manager, title, desc, &item_tooltip) + .with_tooltip(self.tooltip_manager, title, &*desc, &item_tooltip) .set(state.ids.tabard_slot, ui); // Mainhand/Left-Slot - let (title, desc) = loadout.active_item.as_ref().map(|i| &i.item).map_or( - (self.localized_strings.get("hud.bag.mainhand"), ""), - |item| (item.name(), item.description()), - ); + let (title, desc) = + loadout_slot_text(loadout.active_item.as_ref().map(|i| &i.item), || { + (i18n.get("hud.bag.mainhand"), "") + }); slot_maker .fabricate(EquipSlot::Mainhand, [85.0; 2]) .bottom_right_with_margins_on(state.ids.back_slot, -95.0, 0.0) .with_icon(self.imgs.mainhand_bg, Vec2::new(75.0, 75.0), Some(UI_MAIN)) - .with_tooltip(self.tooltip_manager, title, desc, &item_tooltip) + .with_tooltip(self.tooltip_manager, title, &*desc, &item_tooltip) .set(state.ids.mainhand_slot, ui); // Offhand/Right-Slot - let (title, desc) = loadout.second_item.as_ref().map(|i| &i.item).map_or( - (self.localized_strings.get("hud.bag.offhand"), ""), - |item| (item.name(), item.description()), - ); + let (title, desc) = + loadout_slot_text(loadout.second_item.as_ref().map(|i| &i.item), || { + (i18n.get("hud.bag.offhand"), "") + }); slot_maker .fabricate(EquipSlot::Offhand, [85.0; 2]) .bottom_left_with_margins_on(state.ids.feet_slot, -95.0, 0.0) .with_icon(self.imgs.offhand_bg, Vec2::new(75.0, 75.0), Some(UI_MAIN)) - .with_tooltip(self.tooltip_manager, title, desc, &item_tooltip) + .with_tooltip(self.tooltip_manager, title, &*desc, &item_tooltip) .set(state.ids.offhand_slot, ui); } else { // Stats @@ -580,11 +542,14 @@ impl<'a> Widget for Bag<'a> { // Divider /*Image::new(self.imgs.divider) .w_h(50.0, 5.0) - .mid_top_with_margin_on(state.ids.exp, 30.0) + .mid_top_with_margin_on(state.ids.exp, 20.0) .color(Some(UI_HIGHLIGHT_0)) .set(state.ids.divider, ui);*/ // Stats + // Defense + let damage_reduction = (100.0 * loadout.get_damage_reduction()) as i32; + Text::new( &self .localized_strings @@ -596,24 +561,29 @@ impl<'a> Widget for Bag<'a> { .color(TEXT_COLOR) .set(state.ids.statnames, ui); Image::new(self.imgs.endurance_ico) - .w_h(30.0, 30.0) - .top_left_with_margins_on(state.ids.statnames, -10.0, -40.0) + .w_h(20.0, 20.0) + .top_left_with_margins_on(state.ids.statnames, 0.0, -40.0) .color(Some(UI_HIGHLIGHT_0)) .set(state.ids.end_ico, ui); Image::new(self.imgs.fitness_ico) - .w_h(30.0, 30.0) - .down_from(state.ids.end_ico, 10.0) + .w_h(20.0, 20.0) + .down_from(state.ids.end_ico, 15.0) .color(Some(UI_HIGHLIGHT_0)) .set(state.ids.fit_ico, ui); Image::new(self.imgs.willpower_ico) - .w_h(30.0, 30.0) - .down_from(state.ids.fit_ico, 10.0) + .w_h(20.0, 20.0) + .down_from(state.ids.fit_ico, 15.0) .color(Some(UI_HIGHLIGHT_0)) .set(state.ids.wp_ico, ui); + Image::new(self.imgs.protection_ico) + .w_h(20.0, 20.0) + .down_from(state.ids.wp_ico, 15.0) + .color(Some(UI_HIGHLIGHT_0)) + .set(state.ids.prot_ico, ui); Text::new(&format!( - "{}\n\n{}\n\n{}", - self.stats.endurance, self.stats.fitness, self.stats.willpower + "{}\n\n{}\n\n{}\n\n{}%", + self.stats.endurance, self.stats.fitness, self.stats.willpower, damage_reduction )) .top_right_with_margins_on(state.ids.stats_alignment, 120.0, 150.0) .font_id(self.fonts.cyri.conrod_id) @@ -662,16 +632,9 @@ impl<'a> Widget for Bag<'a> { 0.0 + x as f64 * (40.0), ); if let Some(item) = item { + let (title, desc) = super::util::item_text(item); slot_widget - .with_tooltip( - self.tooltip_manager, - &item.name(), - &format!( - "{}", - /* item.kind, item.effect(), */ item.description() - ), - &item_tooltip, - ) + .with_tooltip(self.tooltip_manager, title, &*desc, &item_tooltip) .set(state.ids.inv_slots[i], ui); } else { slot_widget.set(state.ids.inv_slots[i], ui); diff --git a/voxygen/src/hud/img_ids.rs b/voxygen/src/hud/img_ids.rs index 17e87924de..240860dd6e 100644 --- a/voxygen/src/hud/img_ids.rs +++ b/voxygen/src/hud/img_ids.rs @@ -241,6 +241,7 @@ image_ids! { willpower_ico: "voxygen.element.icons.willpower", endurance_ico: "voxygen.element.icons.endurance", fitness_ico: "voxygen.element.icons.fitness", + protection_ico: "voxygen.element.icons.protection", not_found:"voxygen.element.not_found", diff --git a/voxygen/src/hud/item_imgs.rs b/voxygen/src/hud/item_imgs.rs index 8661e911fe..53f55d1b1f 100644 --- a/voxygen/src/hud/item_imgs.rs +++ b/voxygen/src/hud/item_imgs.rs @@ -2,7 +2,7 @@ use crate::ui::{Graphic, SampleStrat, Transform, Ui}; use common::{ assets::{self, watch::ReloadIndicator, Asset}, comp::item::{ - armor::Armor, + armor::{Armor, ArmorKind}, tool::{Tool, ToolKind}, Consumable, Ingredient, Item, ItemKind, Lantern, LanternKind, Throwable, Utility, }, @@ -21,7 +21,7 @@ use vek::*; pub enum ItemKey { Tool(ToolKind), Lantern(LanternKind), - Armor(Armor), + Armor(ArmorKind), Utility(Utility), Consumable(Consumable), Throwable(Throwable), @@ -33,7 +33,7 @@ impl From<&Item> for ItemKey { match &item.kind { ItemKind::Tool(Tool { kind, .. }) => ItemKey::Tool(*kind), ItemKind::Lantern(Lantern { kind, .. }) => ItemKey::Lantern(*kind), - ItemKind::Armor { kind, .. } => ItemKey::Armor(*kind), + ItemKind::Armor(Armor { kind, .. }) => ItemKey::Armor(*kind), ItemKind::Utility { kind, .. } => ItemKey::Utility(*kind), ItemKind::Consumable { kind, .. } => ItemKey::Consumable(*kind), ItemKind::Throwable { kind, .. } => ItemKey::Throwable(*kind), diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index 89bf34089c..7a4c2a75f6 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -15,6 +15,7 @@ mod skillbar; mod slots; mod social; mod spell; +mod util; use crate::{ecs::comp::HpFloaterList, hud::img_ids::ImgsRot, ui::img_ids::Rotations}; pub use hotbar::{SlotContents as HotbarSlotContents, State as HotbarState}; diff --git a/voxygen/src/hud/util.rs b/voxygen/src/hud/util.rs new file mode 100644 index 0000000000..5b8c15cbb9 --- /dev/null +++ b/voxygen/src/hud/util.rs @@ -0,0 +1,85 @@ +use common::comp::item::{ + armor::{Armor, ArmorKind, Protection}, + Item, ItemKind, +}; +use std::borrow::Cow; + +pub fn loadout_slot_text<'a>( + item: Option<&'a Item>, + mut empty: impl FnMut() -> (&'a str, &'a str), +) -> (&'a str, Cow<'a, str>) { + item.map_or_else( + || { + let (title, desc) = empty(); + (title, Cow::Borrowed(desc)) + }, + item_text, + ) +} + +pub fn item_text<'a>(item: &'a Item) -> (&'_ str, Cow<'a, str>) { + let desc = match item.kind { + ItemKind::Armor(armor) => Cow::Owned(armor_desc(armor, item.description())), + // ItemKind::Tool => {}, + /*ItemKind::Consumable(kind, effect, ..) => { + Cow::Owned(consumable_desc(consumable, item.description())) + },*/ + // ItemKind::Throwable => {}, + // ItemKind::Utility => {}, + // ItemKind::Ingredient => {}, + // ItemKind::Lantern => {}, + _ => Cow::Borrowed(item.description()), + }; + + (item.name(), desc) +} +// Armor Description +fn armor_desc(armor: Armor, desc: &str) -> String { + // TODO: localization + let kind = match armor.kind { + ArmorKind::Shoulder(_) => "Shoulders", + ArmorKind::Chest(_) => "Chest", + ArmorKind::Belt(_) => "Belt", + ArmorKind::Hand(_) => "Hands", + ArmorKind::Pants(_) => "Legs", + ArmorKind::Foot(_) => "Feet", + ArmorKind::Back(_) => "Back", + ArmorKind::Ring(_) => "Ring", + ArmorKind::Neck(_) => "Neck", + ArmorKind::Head(_) => "Head", + ArmorKind::Tabard(_) => "Tabard", + }; + let armor = match armor.get_protection() { + Protection::Normal(a) => a.to_string(), + Protection::Invincible => "Inf".to_string(), + }; + + if !desc.is_empty() { + format!( + "{}\n\nArmor: {}\n\n{}\n\n", + kind, armor, desc + ) + } else { + format!("{}\n\nArmor: {}\n\n", kind, armor) + } +} +// Weapon/Tool Description + +// Consumable Description +/*fn consumable_desc(consumable: Consumable, desc: &str) -> String { + // TODO: localization + let kind = "Consumable"; + if !desc.is_empty() { + format!("{}\n\n{}\n\n", kind, desc) + } else { + format!("{}\n\n", kind) + } +}*/ + +// Throwable Description + +// Utility Description + +// Ingredient Description + +// Lantern Description diff --git a/voxygen/src/scene/figure/cache.rs b/voxygen/src/scene/figure/cache.rs index 09faea2a4f..20f4f0ea48 100644 --- a/voxygen/src/scene/figure/cache.rs +++ b/voxygen/src/scene/figure/cache.rs @@ -8,8 +8,8 @@ use anim::Skeleton; use common::{ assets::watch::ReloadIndicator, comp::{ - item::{tool::ToolKind, ItemKind}, - Body, CharacterState, Item, Loadout, + item::{armor::ArmorKind, tool::ToolKind, ItemKind, LanternKind}, + Body, CharacterState, Loadout, }, figure::Segment, vol::BaseVol, @@ -33,14 +33,14 @@ struct CharacterCacheKey { state: Option>, // TODO: Can this be simplified? active_tool: Option, second_tool: Option, - shoulder: Option, - chest: Option, - belt: Option, - back: Option, - lantern: Option, - hand: Option, - pants: Option, - foot: Option, + shoulder: Option, + chest: Option, + belt: Option, + back: Option, + lantern: Option, + hand: Option, + pants: Option, + foot: Option, } impl CharacterCacheKey { @@ -61,14 +61,50 @@ impl CharacterCacheKey { } else { None }, - shoulder: loadout.shoulder.clone(), - chest: loadout.chest.clone(), - belt: loadout.belt.clone(), - back: loadout.back.clone(), - lantern: loadout.lantern.clone(), - hand: loadout.hand.clone(), - pants: loadout.pants.clone(), - foot: loadout.foot.clone(), + shoulder: if let Some(ItemKind::Armor(armor)) = + loadout.shoulder.as_ref().map(|i| &i.kind) + { + Some(armor.kind) + } else { + None + }, + chest: if let Some(ItemKind::Armor(armor)) = loadout.chest.as_ref().map(|i| &i.kind) { + Some(armor.kind) + } else { + None + }, + belt: if let Some(ItemKind::Armor(armor)) = loadout.belt.as_ref().map(|i| &i.kind) { + Some(armor.kind) + } else { + None + }, + back: if let Some(ItemKind::Armor(armor)) = loadout.back.as_ref().map(|i| &i.kind) { + Some(armor.kind) + } else { + None + }, + lantern: if let Some(ItemKind::Lantern(lantern)) = + loadout.lantern.as_ref().map(|i| &i.kind) + { + Some(lantern.kind) + } else { + None + }, + hand: if let Some(ItemKind::Armor(armor)) = loadout.hand.as_ref().map(|i| &i.kind) { + Some(armor.kind) + } else { + None + }, + pants: if let Some(ItemKind::Armor(armor)) = loadout.pants.as_ref().map(|i| &i.kind) { + Some(armor.kind) + } else { + None + }, + foot: if let Some(ItemKind::Armor(armor)) = loadout.foot.as_ref().map(|i| &i.kind) { + Some(armor.kind) + } else { + None + }, } } } diff --git a/voxygen/src/scene/figure/load.rs b/voxygen/src/scene/figure/load.rs index cf1f324a1d..ab5fdb8e7b 100644 --- a/voxygen/src/scene/figure/load.rs +++ b/voxygen/src/scene/figure/load.rs @@ -11,7 +11,9 @@ use common::{ golem::{BodyType as GBodyType, Species as GSpecies}, humanoid::{Body, BodyType, EyeColor, Skin, Species}, item::{ - armor::{Armor, Back, Belt, Chest, Foot, Hand, Head, Pants, Shoulder, Tabard}, + armor::{ + Armor, ArmorKind, Back, Belt, Chest, Foot, Hand, Head, Pants, Shoulder, Tabard, + }, tool::{Tool, ToolKind}, ItemKind, Lantern, LanternKind, }, @@ -365,10 +367,10 @@ impl HumArmorShoulderSpec { flipped: bool, generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, ) -> Mesh { - let spec = if let Some(ItemKind::Armor { - kind: Armor::Shoulder(shoulder), + let spec = if let Some(ItemKind::Armor(Armor { + kind: ArmorKind::Shoulder(shoulder), .. - }) = loadout.shoulder.as_ref().map(|i| &i.kind) + })) = loadout.shoulder.as_ref().map(|i| &i.kind) { match self.0.map.get(&shoulder) { Some(spec) => spec, @@ -447,10 +449,10 @@ impl HumArmorChestSpec { loadout: &Loadout, generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, ) -> Mesh { - let spec = if let Some(ItemKind::Armor { - kind: Armor::Chest(chest), + let spec = if let Some(ItemKind::Armor(Armor { + kind: ArmorKind::Chest(chest), .. - }) = loadout.chest.as_ref().map(|i| &i.kind) + })) = loadout.chest.as_ref().map(|i| &i.kind) { match self.0.map.get(&chest) { Some(spec) => spec, @@ -504,10 +506,10 @@ impl HumArmorHandSpec { flipped: bool, generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, ) -> Mesh { - let spec = if let Some(ItemKind::Armor { - kind: Armor::Hand(hand), + let spec = if let Some(ItemKind::Armor(Armor { + kind: ArmorKind::Hand(hand), .. - }) = loadout.hand.as_ref().map(|i| &i.kind) + })) = loadout.hand.as_ref().map(|i| &i.kind) { match self.0.map.get(&hand) { Some(spec) => spec, @@ -580,10 +582,10 @@ impl HumArmorBeltSpec { loadout: &Loadout, generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, ) -> Mesh { - let spec = if let Some(ItemKind::Armor { - kind: Armor::Belt(belt), + let spec = if let Some(ItemKind::Armor(Armor { + kind: ArmorKind::Belt(belt), .. - }) = loadout.belt.as_ref().map(|i| &i.kind) + })) = loadout.belt.as_ref().map(|i| &i.kind) { match self.0.map.get(&belt) { Some(spec) => spec, @@ -624,10 +626,10 @@ impl HumArmorBackSpec { loadout: &Loadout, generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, ) -> Mesh { - let spec = if let Some(ItemKind::Armor { - kind: Armor::Back(back), + let spec = if let Some(ItemKind::Armor(Armor { + kind: ArmorKind::Back(back), .. - }) = loadout.back.as_ref().map(|i| &i.kind) + })) = loadout.back.as_ref().map(|i| &i.kind) { match self.0.map.get(&back) { Some(spec) => spec, @@ -667,10 +669,10 @@ impl HumArmorPantsSpec { loadout: &Loadout, generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, ) -> Mesh { - let spec = if let Some(ItemKind::Armor { - kind: Armor::Pants(pants), + let spec = if let Some(ItemKind::Armor(Armor { + kind: ArmorKind::Pants(pants), .. - }) = loadout.pants.as_ref().map(|i| &i.kind) + })) = loadout.pants.as_ref().map(|i| &i.kind) { match self.0.map.get(&pants) { Some(spec) => spec, @@ -724,10 +726,10 @@ impl HumArmorFootSpec { flipped: bool, generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, ) -> Mesh { - let spec = if let Some(ItemKind::Armor { - kind: Armor::Foot(foot), + let spec = if let Some(ItemKind::Armor(Armor { + kind: ArmorKind::Foot(foot), .. - }) = loadout.foot.as_ref().map(|i| &i.kind) + })) = loadout.foot.as_ref().map(|i| &i.kind) { match self.0.map.get(&foot) { Some(spec) => spec, @@ -879,10 +881,10 @@ impl HumArmorHeadSpec { loadout: &Loadout, generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, ) -> Mesh { - let spec = if let Some(ItemKind::Armor { - kind: Armor::Head(head), + let spec = if let Some(ItemKind::Armor(Armor { + kind: ArmorKind::Head(head), .. - }) = loadout.head.as_ref().map(|i| &i.kind) + })) = loadout.head.as_ref().map(|i| &i.kind) { match self.0.map.get(&head) { Some(spec) => spec, @@ -934,10 +936,10 @@ impl HumArmorTabardSpec { loadout: &Loadout, generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, ) -> Mesh { - let spec = if let Some(ItemKind::Armor { - kind: Armor::Tabard(tabard), + let spec = if let Some(ItemKind::Armor(Armor { + kind: ArmorKind::Tabard(tabard), .. - }) = loadout.tabard.as_ref().map(|i| &i.kind) + })) = loadout.tabard.as_ref().map(|i| &i.kind) { match self.0.map.get(&tabard) { Some(spec) => spec, From 807a27328c392670d3e28055b15eed1b0ba6e59d Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Sat, 26 Oct 2019 15:50:01 +0100 Subject: [PATCH 43/85] Updated to winit 20 --- Cargo.lock | 163 ++++++++++++++++++++++++++++++++++------ voxygen/Cargo.toml | 8 +- voxygen/src/settings.rs | 2 +- voxygen/src/ui/event.rs | 12 +-- voxygen/src/window.rs | 96 ++++++++++++----------- 5 files changed, 204 insertions(+), 77 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a847fa1d48..6155708ef3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -428,6 +428,17 @@ dependencies = [ "winapi 0.3.8", ] +[[package]] +name = "calloop" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7aa2097be53a00de9e8fc349fea6d76221f398f5c4fa550d420669906962d160" +dependencies = [ + "mio", + "mio-extras", + "nix 0.14.1", +] + [[package]] name = "cast" version = "0.2.3" @@ -471,6 +482,15 @@ dependencies = [ "libc", ] +[[package]] +name = "cgl" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ced0551234e87afee12411d535648dd89d2e7f34c78b753395567aff3d447ff" +dependencies = [ + "libc", +] + [[package]] name = "chrono" version = "0.4.11" @@ -536,8 +556,8 @@ checksum = "1706996401131526e36b3b49f0c4d912639ce110996f3ca144d78946727bce54" dependencies = [ "bitflags", "block", - "core-foundation", - "core-graphics", + "core-foundation 0.6.4", + "core-graphics 0.17.3", "foreign-types", "libc", "objc", @@ -551,8 +571,8 @@ checksum = "f29f7768b2d1be17b96158e3285951d366b40211320fb30826a76cb7a0da6400" dependencies = [ "bitflags", "block", - "core-foundation", - "core-graphics", + "core-foundation 0.6.4", + "core-graphics 0.17.3", "foreign-types", "libc", "objc", @@ -561,7 +581,7 @@ dependencies = [ [[package]] name = "conrod_core" version = "0.63.0" -source = "git+https://gitlab.com/veloren/conrod.git?branch=pre-winit-20#46b374edc9537300e5278905ebd14dff45cfd927" +source = "git+https://gitlab.com/veloren/conrod.git?branch=hide_text#92925b122dfed139169c7d7687b3ca15c0cb5a2e" dependencies = [ "conrod_derive", "copypasta", @@ -576,7 +596,7 @@ dependencies = [ [[package]] name = "conrod_derive" version = "0.63.0" -source = "git+https://gitlab.com/veloren/conrod.git?branch=pre-winit-20#46b374edc9537300e5278905ebd14dff45cfd927" +source = "git+https://gitlab.com/veloren/conrod.git?branch=hide_text#92925b122dfed139169c7d7687b3ca15c0cb5a2e" dependencies = [ "proc-macro2 0.4.30", "quote 0.6.13", @@ -586,7 +606,7 @@ dependencies = [ [[package]] name = "conrod_winit" version = "0.63.0" -source = "git+https://gitlab.com/veloren/conrod.git?branch=pre-winit-20#46b374edc9537300e5278905ebd14dff45cfd927" +source = "git+https://gitlab.com/veloren/conrod.git?branch=hide_text#92925b122dfed139169c7d7687b3ca15c0cb5a2e" [[package]] name = "const-random" @@ -673,7 +693,17 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25b9e03f145fd4f2bf705e07b900cd41fc636598fe5dc452fd0db1441c3f496d" dependencies = [ - "core-foundation-sys", + "core-foundation-sys 0.6.2", + "libc", +] + +[[package]] +name = "core-foundation" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57d24c7a13c43e870e37c1556b74555437870a04514f7685f5b354e090567171" +dependencies = [ + "core-foundation-sys 0.7.0", "libc", ] @@ -683,6 +713,12 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7ca8a5221364ef15ce201e8ed2f609fc312682a8f4e0e3d4aa5879764e0fa3b" +[[package]] +name = "core-foundation-sys" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3a71ab494c0b5b860bdc8407ae08978052417070c2ced38573a9157ad75b8ac" + [[package]] name = "core-graphics" version = "0.17.3" @@ -690,11 +726,36 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56790968ab1c8a1202a102e6de05fc6e1ec87da99e4e93e9a7d13efbfc1e95a9" dependencies = [ "bitflags", - "core-foundation", + "core-foundation 0.6.4", "foreign-types", "libc", ] +[[package]] +name = "core-graphics" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3889374e6ea6ab25dba90bb5d96202f61108058361f6dc72e8b03e6f8bbe923" +dependencies = [ + "bitflags", + "core-foundation 0.7.0", + "foreign-types", + "libc", +] + +[[package]] +name = "core-video-sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34ecad23610ad9757664d644e369246edde1803fcb43ed72876565098a5d3828" +dependencies = [ + "cfg-if", + "core-foundation-sys 0.7.0", + "core-graphics 0.19.2", + "libc", + "objc", +] + [[package]] name = "coreaudio-rs" version = "0.9.1" @@ -721,7 +782,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b55d55d69f403f62a95bd3c04b431e0aedf5120c70f15d07a8edd234443dd59" dependencies = [ "alsa-sys", - "core-foundation-sys", + "core-foundation-sys 0.6.2", "coreaudio-rs", "lazy_static", "libc", @@ -1513,7 +1574,7 @@ checksum = "310ff66f08b5a55854b18fea2f48bdbb75c94458207ba574c9723be78e97a646" dependencies = [ "gfx_core", "gfx_device_gl", - "glutin", + "glutin 0.21.2", ] [[package]] @@ -1537,7 +1598,7 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43c758daf46af26d6872fe55507e3b2339779a160a06ad7a9b2a082f221209cd" dependencies = [ - "core-foundation", + "core-foundation 0.6.4", "io-kit-sys", "libc", "libudev-sys", @@ -1677,10 +1738,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5371b35b309dace06be1b81b5f6adb1c9de578b7dbe1e74bf7e4ef762cf6febd" dependencies = [ "android_glue", - "cgl", + "cgl 0.2.3", "cocoa 0.18.5", - "core-foundation", - "core-graphics", + "core-foundation 0.6.4", + "core-graphics 0.17.3", "glutin_egl_sys", "glutin_emscripten_sys", "glutin_gles2_sys", @@ -1693,7 +1754,34 @@ dependencies = [ "parking_lot 0.9.0", "wayland-client 0.21.13", "winapi 0.3.8", - "winit", + "winit 0.19.5", +] + +[[package]] +name = "glutin" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "639246c8838b02a83b9339bd87da3714e73e52ecfaa758e15d761eb77b2290b5" +dependencies = [ + "android_glue", + "cgl 0.3.2", + "cocoa 0.19.1", + "core-foundation 0.6.4", + "core-graphics 0.17.3", + "glutin_egl_sys", + "glutin_emscripten_sys", + "glutin_gles2_sys", + "glutin_glx_sys", + "glutin_wgl_sys", + "lazy_static", + "libloading 0.5.2", + "log", + "objc", + "osmesa-sys", + "parking_lot 0.10.2", + "wayland-client 0.23.6", + "winapi 0.3.8", + "winit 0.20.0", ] [[package]] @@ -2048,7 +2136,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f21dcc74995dd4cd090b147e79789f8d65959cbfb5f0b118002db869ea3bd0a0" dependencies = [ - "core-foundation-sys", + "core-foundation-sys 0.6.2", "mach", ] @@ -4516,7 +4604,7 @@ dependencies = [ "gilrs", "git2", "glsl-include", - "glutin", + "glutin 0.22.1", "guillotiere", "hashbrown", "image", @@ -4541,7 +4629,7 @@ dependencies = [ "veloren-server", "veloren-voxygen-anim", "veloren-world", - "winit", + "winit 0.20.0", "winres", ] @@ -4728,8 +4816,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af1080ebe0efabcf12aef2132152f616038f2d7dcbbccf7b2d8c5270fe14bcda" dependencies = [ "bitflags", + "calloop", "downcast-rs", "libc", + "mio", "nix 0.14.1", "wayland-commons 0.23.6", "wayland-scanner 0.23.6", @@ -4905,8 +4995,8 @@ dependencies = [ "backtrace", "bitflags", "cocoa 0.18.5", - "core-foundation", - "core-graphics", + "core-foundation 0.6.4", + "core-graphics 0.17.3", "lazy_static", "libc", "log", @@ -4914,13 +5004,42 @@ dependencies = [ "parking_lot 0.9.0", "percent-encoding 2.1.0", "raw-window-handle", - "serde", "smithay-client-toolkit 0.4.6", "wayland-client 0.21.13", "winapi 0.3.8", "x11-dl", ] +[[package]] +name = "winit" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ba128780050481f453bec2a115b916dbc6ae79c303dee9bad8b9080bdccd4f5" +dependencies = [ + "android_glue", + "bitflags", + "cocoa 0.19.1", + "core-foundation 0.6.4", + "core-graphics 0.17.3", + "core-video-sys", + "dispatch", + "instant", + "lazy_static", + "libc", + "log", + "mio", + "mio-extras", + "objc", + "parking_lot 0.10.2", + "percent-encoding 2.1.0", + "raw-window-handle", + "serde", + "smithay-client-toolkit 0.6.6", + "wayland-client 0.23.6", + "winapi 0.3.8", + "x11-dl", +] + [[package]] name = "winres" version = "0.1.11" diff --git a/voxygen/Cargo.toml b/voxygen/Cargo.toml index 9fcc062af1..2e8e64ad7e 100644 --- a/voxygen/Cargo.toml +++ b/voxygen/Cargo.toml @@ -26,10 +26,10 @@ anim = { package = "veloren-voxygen-anim", path = "src/anim", default-features = gfx = "0.18.2" gfx_device_gl = { version = "0.16.2", optional = true } gfx_window_glutin = "0.31.0" -glutin = "0.21.1" -winit = { version = "0.19.4", features = ["serde"] } -conrod_core = { git = "https://gitlab.com/veloren/conrod.git", branch = "pre-winit-20" } -conrod_winit = { git = "https://gitlab.com/veloren/conrod.git", branch = "pre-winit-20" } +glutin = "0.22.0-alpha3" +winit = { version = "0.20.0-alpha4", features = ["serde"] } +conrod_core = { git = "https://gitlab.com/veloren/conrod.git", branch = "hide_text" } +conrod_winit = { git = "https://gitlab.com/veloren/conrod.git", branch = "hide_text" } euc = { git = "https://github.com/zesterer/euc.git" } # ECS diff --git a/voxygen/src/settings.rs b/voxygen/src/settings.rs index fb6f787e96..3aec021d4c 100644 --- a/voxygen/src/settings.rs +++ b/voxygen/src/settings.rs @@ -6,11 +6,11 @@ use crate::{ window::{GameInput, KeyMouse}, }; use directories_next::{ProjectDirs, UserDirs}; -use glutin::{MouseButton, VirtualKeyCode}; use hashbrown::{HashMap, HashSet}; use serde_derive::{Deserialize, Serialize}; use std::{fs, io::prelude::*, path::PathBuf}; use tracing::warn; +use winit::event::{MouseButton, VirtualKeyCode}; // ControlSetting-like struct used by Serde, to handle not serializing/building // post-deserializing the inverse_keybindings hashmap diff --git a/voxygen/src/ui/event.rs b/voxygen/src/ui/event.rs index b6a6dc20d7..e7c3b6f2bb 100644 --- a/voxygen/src/ui/event.rs +++ b/voxygen/src/ui/event.rs @@ -5,24 +5,24 @@ use vek::*; pub struct Event(pub Input); impl Event { pub fn try_from( - event: glutin::Event, - window: &glutin::ContextWrapper, + event: glutin::event::Event<()>, + window: &glutin::ContextWrapper, ) -> Option { use conrod_winit::*; // A wrapper around the winit window that allows us to implement the trait // necessary for enabling the winit <-> conrod conversion functions. - struct WindowRef<'a>(&'a winit::Window); + struct WindowRef<'a>(&'a winit::window::Window); // Implement the `WinitWindow` trait for `WindowRef` to allow for generating // compatible conversion functions. impl<'a> conrod_winit::WinitWindow for WindowRef<'a> { fn get_inner_size(&self) -> Option<(u32, u32)> { - winit::Window::get_inner_size(&self.0).map(Into::into) + Some(winit::window::Window::inner_size(&self.0).into()) } - fn hidpi_factor(&self) -> f32 { winit::Window::get_hidpi_factor(&self.0) as _ } + fn hidpi_factor(&self) -> f32 { winit::window::Window::get_hidpi_factor(&self.0) as _ } } - convert_event!(event, &WindowRef(window.window())).map(Self) + convert_event!(event, &WindowRef(window.window())).map(|input| Self(input)) } pub fn is_keyboard_or_mouse(&self) -> bool { diff --git a/voxygen/src/window.rs b/voxygen/src/window.rs index 73b75de715..e5f078253f 100644 --- a/voxygen/src/window.rs +++ b/voxygen/src/window.rs @@ -280,19 +280,19 @@ pub enum Event { ScreenshotMessage(String), } -pub type MouseButton = winit::MouseButton; -pub type PressState = winit::ElementState; +pub type MouseButton = winit::event::MouseButton; +pub type PressState = winit::event::ElementState; #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)] pub enum KeyMouse { - Key(glutin::VirtualKeyCode), - Mouse(glutin::MouseButton), + Key(glutin::event::VirtualKeyCode), + Mouse(glutin::event::MouseButton), } impl fmt::Display for KeyMouse { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { use self::KeyMouse::*; - use glutin::{MouseButton, VirtualKeyCode::*}; + use glutin::event::{MouseButton, VirtualKeyCode::*}; write!(f, "{}", match self { Key(Key1) => "1", Key(Key2) => "2", @@ -466,9 +466,9 @@ impl fmt::Display for KeyMouse { } pub struct Window { - events_loop: glutin::EventsLoop, + events_loop: glutin::event_loop::EventLoop<()>, renderer: Renderer, - window: glutin::ContextWrapper, + window: glutin::ContextWrapper, cursor_grabbed: bool, pub pan_sensitivity: u32, pub zoom_sensitivity: u32, @@ -476,7 +476,7 @@ pub struct Window { pub mouse_y_inversion: bool, fullscreen: bool, needs_refresh_resize: bool, - keypress_map: HashMap, + keypress_map: HashMap, pub remapping_keybindings: Option, supplement_events: Vec, focused: bool, @@ -491,11 +491,11 @@ pub struct Window { impl Window { pub fn new(settings: &Settings) -> Result { - let events_loop = glutin::EventsLoop::new(); + let events_loop = glutin::event_loop::EventLoop::new(); let size = settings.graphics.window_size; - let win_builder = glutin::WindowBuilder::new() + let win_builder = glutin::window::WindowBuilder::new() .with_title("Veloren") .with_dimensions(glutin::dpi::LogicalSize::new( size[0] as f64, @@ -644,19 +644,22 @@ impl Window { } match event { - glutin::Event::WindowEvent { event, .. } => match event { - glutin::WindowEvent::CloseRequested => events.push(Event::Close), - glutin::WindowEvent::Resized(glutin::dpi::LogicalSize { width, height }) => { + glutin::event::Event::WindowEvent { event, .. } => match event { + glutin::event::WindowEvent::CloseRequested => events.push(Event::Close), + glutin::event::WindowEvent::Resized(glutin::dpi::LogicalSize { + width, + height, + }) => { let (mut color_view, mut depth_view) = renderer.win_views_mut(); gfx_window_glutin::update_views(window, &mut color_view, &mut depth_view); renderer.on_resize().unwrap(); events.push(Event::Resize(Vec2::new(width as u32, height as u32))); }, - glutin::WindowEvent::Moved(glutin::dpi::LogicalPosition { x, y }) => { + glutin::event::WindowEvent::Moved(glutin::dpi::LogicalPosition { x, y }) => { events.push(Event::Moved(Vec2::new(x as u32, y as u32))) }, - glutin::WindowEvent::ReceivedCharacter(c) => events.push(Event::Char(c)), - glutin::WindowEvent::MouseInput { button, state, .. } => { + glutin::event::WindowEvent::ReceivedCharacter(c) => events.push(Event::Char(c)), + glutin::event::WindowEvent::MouseInput { button, state, .. } => { if let (true, Some(game_inputs)) = ( cursor_grabbed, Window::map_input( @@ -668,13 +671,13 @@ impl Window { for game_input in game_inputs { events.push(Event::InputUpdate( *game_input, - state == glutin::ElementState::Pressed, + state == glutin::event::ElementState::Pressed, )); } } events.push(Event::MouseButton(button, state)); }, - glutin::WindowEvent::KeyboardInput { input, .. } => { + glutin::event::WindowEvent::KeyboardInput { input, .. } => { if let Some(key) = input.virtual_keycode { if let Some(game_inputs) = Window::map_input( KeyMouse::Key(key), @@ -684,7 +687,7 @@ impl Window { for game_input in game_inputs { match game_input { GameInput::Fullscreen => { - if input.state == glutin::ElementState::Pressed + if input.state == glutin::event::ElementState::Pressed && !Self::is_pressed( keypress_map, GameInput::Fullscreen, @@ -700,7 +703,7 @@ impl Window { }, GameInput::Screenshot => { take_screenshot = input.state - == glutin::ElementState::Pressed + == glutin::event::ElementState::Pressed && !Self::is_pressed( keypress_map, GameInput::Screenshot, @@ -713,25 +716,24 @@ impl Window { }, _ => events.push(Event::InputUpdate( *game_input, - input.state == glutin::ElementState::Pressed, + input.state == glutin::event::ElementState::Pressed, )), } } } } }, - - glutin::WindowEvent::Focused(state) => { + glutin::event::WindowEvent::Focused(state) => { *focused = state; events.push(Event::Focused(state)); }, - glutin::WindowEvent::CursorMoved { position, .. } => { + glutin::event::WindowEvent::CursorMoved { position, .. } => { cursor_position = Some(position); }, _ => {}, }, - glutin::Event::DeviceEvent { event, .. } => match event { - glutin::DeviceEvent::MouseMotion { + glutin::event::Event::DeviceEvent { event, .. } => match event { + glutin::event::DeviceEvent::MouseMotion { delta: (dx, dy), .. } if *focused => { let delta = Vec2::new( @@ -745,7 +747,9 @@ impl Window { events.push(Event::CursorMove(delta)); } }, - glutin::DeviceEvent::MouseWheel { delta, .. } if cursor_grabbed && *focused => { + glutin::event::DeviceEvent::MouseWheel { delta, .. } + if cursor_grabbed && *focused => + { events.push(Event::Zoom({ // Since scrolling apparently acts different depending on platform #[cfg(target_os = "windows")] @@ -754,17 +758,19 @@ impl Window { const PLATFORM_FACTOR: f32 = 1.0; let y = match delta { - glutin::MouseScrollDelta::LineDelta(_x, y) => y, + glutin::event::MouseScrollDelta::LineDelta(_x, y) => y, // TODO: Check to see if there is a better way to find the "line // height" than just hardcoding 16.0 pixels. Alternately we could // get rid of this and have the user set zoom sensitivity, since // it's unlikely people would expect a configuration file to work // across operating systems. - glutin::MouseScrollDelta::PixelDelta(pos) => (pos.y / 16.0) as f32, + glutin::event::MouseScrollDelta::PixelDelta(pos) => { + (pos.y / 16.0) as f32 + }, }; y * (zoom_sensitivity as f32 / 100.0) * zoom_inversion * PLATFORM_FACTOR })) - }, + } _ => {}, }, _ => {}, @@ -1004,8 +1010,8 @@ impl Window { pub fn grab_cursor(&mut self, grab: bool) { self.cursor_grabbed = grab; - self.window.window().hide_cursor(grab); - let _ = self.window.window().grab_cursor(grab); + self.window.window().set_cursor_visible(!grab); + let _ = self.window.window().set_cursor_grab(grab); } pub fn toggle_fullscreen(&mut self, settings: &mut Settings) { @@ -1020,7 +1026,9 @@ impl Window { let window = self.window.window(); self.fullscreen = fullscreen; if fullscreen { - window.set_fullscreen(Some(window.get_current_monitor())); + window.set_fullscreen(Some(winit::window::Fullscreen::Borderless( + window.current_monitor(), + ))); } else { window.set_fullscreen(None); } @@ -1030,12 +1038,7 @@ impl Window { #[allow(clippy::or_fun_call)] // TODO: Pending review in #587 pub fn logical_size(&self) -> Vec2 { - let (w, h) = self - .window - .window() - .get_inner_size() - .unwrap_or(glutin::dpi::LogicalSize::new(0.0, 0.0)) - .into(); + let (w, h) = self.window.window().inner_size().into(); Vec2::new(w, h) } @@ -1086,15 +1089,20 @@ impl Window { } } - fn is_pressed(map: &mut HashMap, input: GameInput) -> bool { - *(map.entry(input).or_insert(glutin::ElementState::Released)) - == glutin::ElementState::Pressed + fn is_pressed( + map: &mut HashMap, + input: GameInput, + ) -> bool { + *(map + .entry(input) + .or_insert(glutin::event::ElementState::Released)) + == glutin::event::ElementState::Pressed } fn set_pressed( - map: &mut HashMap, + map: &mut HashMap, input: GameInput, - state: glutin::ElementState, + state: glutin::event::ElementState, ) { map.insert(input, state); } From 515dbc30a7c538ce89f36d9372c7724d72b92a70 Mon Sep 17 00:00:00 2001 From: Imbris Date: Sat, 26 Oct 2019 21:14:37 -0400 Subject: [PATCH 44/85] Switch to updated gfx_window_glutin --- Cargo.lock | 208 +++++------------------------------------- voxygen/Cargo.toml | 4 +- voxygen/src/window.rs | 2 +- 3 files changed, 26 insertions(+), 188 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6155708ef3..b7635baa76 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -472,16 +472,6 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" -[[package]] -name = "cgl" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55e7ec0b74fe5897894cbc207092c577e87c52f8a59e8ca8d97ef37551f60a49" -dependencies = [ - "gleam", - "libc", -] - [[package]] name = "cgl" version = "0.3.2" @@ -548,21 +538,6 @@ dependencies = [ "bitflags", ] -[[package]] -name = "cocoa" -version = "0.18.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1706996401131526e36b3b49f0c4d912639ce110996f3ca144d78946727bce54" -dependencies = [ - "bitflags", - "block", - "core-foundation 0.6.4", - "core-graphics 0.17.3", - "foreign-types", - "libc", - "objc", -] - [[package]] name = "cocoa" version = "0.19.1" @@ -683,7 +658,7 @@ dependencies = [ "objc-foundation", "objc_id", "smithay-clipboard", - "wayland-client 0.23.6", + "wayland-client", "x11-clipboard", ] @@ -1569,12 +1544,11 @@ dependencies = [ [[package]] name = "gfx_window_glutin" version = "0.31.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "310ff66f08b5a55854b18fea2f48bdbb75c94458207ba574c9723be78e97a646" +source = "git+https://github.com/Imberflur/gfx_window_glutin.git#6586513e6b22924e61d1106e426768ac4f1cb55d" dependencies = [ "gfx_core", "gfx_device_gl", - "glutin 0.21.2", + "glutin", ] [[package]] @@ -1682,15 +1656,6 @@ dependencies = [ "xml-rs", ] -[[package]] -name = "gleam" -version = "0.6.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cae10d7c99d0e77b4766e850a60898a17c1abaf01075531f1066f03dc7dc5fc5" -dependencies = [ - "gl_generator 0.13.1", -] - [[package]] name = "glib" version = "0.5.0" @@ -1731,32 +1696,6 @@ dependencies = [ "regex", ] -[[package]] -name = "glutin" -version = "0.21.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5371b35b309dace06be1b81b5f6adb1c9de578b7dbe1e74bf7e4ef762cf6febd" -dependencies = [ - "android_glue", - "cgl 0.2.3", - "cocoa 0.18.5", - "core-foundation 0.6.4", - "core-graphics 0.17.3", - "glutin_egl_sys", - "glutin_emscripten_sys", - "glutin_gles2_sys", - "glutin_glx_sys", - "glutin_wgl_sys", - "lazy_static", - "libloading 0.5.2", - "objc", - "osmesa-sys", - "parking_lot 0.9.0", - "wayland-client 0.21.13", - "winapi 0.3.8", - "winit 0.19.5", -] - [[package]] name = "glutin" version = "0.22.1" @@ -1764,8 +1703,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "639246c8838b02a83b9339bd87da3714e73e52ecfaa758e15d761eb77b2290b5" dependencies = [ "android_glue", - "cgl 0.3.2", - "cocoa 0.19.1", + "cgl", + "cocoa", "core-foundation 0.6.4", "core-graphics 0.17.3", "glutin_egl_sys", @@ -1779,9 +1718,9 @@ dependencies = [ "objc", "osmesa-sys", "parking_lot 0.10.2", - "wayland-client 0.23.6", + "wayland-client", "winapi 0.3.8", - "winit 0.20.0", + "winit", ] [[package]] @@ -2530,7 +2469,7 @@ name = "msgbox" version = "0.4.0" source = "git+https://github.com/bekker/msgbox-rs.git?rev=68fe39a#68fe39a60019b38a1569ae4e9ed796a0f0542673" dependencies = [ - "cocoa 0.19.1", + "cocoa", "gtk", "objc", "winapi 0.3.8", @@ -3759,23 +3698,6 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7cb5678e1615754284ec264d9bb5b4c27d2018577fd90ac0ceb578591ed5ee4" -[[package]] -name = "smithay-client-toolkit" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ccb8c57049b2a34d2cc2b203fa785020ba0129d31920ef0d317430adaf748fa" -dependencies = [ - "andrew", - "bitflags", - "dlib", - "lazy_static", - "memmap", - "nix 0.14.1", - "wayland-client 0.21.13", - "wayland-commons 0.21.13", - "wayland-protocols 0.21.13", -] - [[package]] name = "smithay-client-toolkit" version = "0.6.6" @@ -3788,8 +3710,8 @@ dependencies = [ "lazy_static", "memmap", "nix 0.14.1", - "wayland-client 0.23.6", - "wayland-protocols 0.23.6", + "wayland-client", + "wayland-protocols", ] [[package]] @@ -3799,7 +3721,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "917e8ec7f535cd1a6cbf749c8866c24d67c548a80ac48c8e88a182eab5c07bd1" dependencies = [ "nix 0.14.1", - "smithay-client-toolkit 0.6.6", + "smithay-client-toolkit", ] [[package]] @@ -4604,7 +4526,7 @@ dependencies = [ "gilrs", "git2", "glsl-include", - "glutin 0.22.1", + "glutin", "guillotiere", "hashbrown", "image", @@ -4629,7 +4551,7 @@ dependencies = [ "veloren-server", "veloren-voxygen-anim", "veloren-world", - "winit 0.20.0", + "winit", "winres", ] @@ -4794,21 +4716,6 @@ version = "0.2.63" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c9ba19973a58daf4db6f352eda73dc0e289493cd29fb2632eb172085b6521acd" -[[package]] -name = "wayland-client" -version = "0.21.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49963e5f9eeaf637bfcd1b9f0701c99fd5cd05225eb51035550d4272806f2713" -dependencies = [ - "bitflags", - "downcast-rs", - "libc", - "nix 0.14.1", - "wayland-commons 0.21.13", - "wayland-scanner 0.21.13", - "wayland-sys 0.21.13", -] - [[package]] name = "wayland-client" version = "0.23.6" @@ -4821,19 +4728,9 @@ dependencies = [ "libc", "mio", "nix 0.14.1", - "wayland-commons 0.23.6", - "wayland-scanner 0.23.6", - "wayland-sys 0.23.6", -] - -[[package]] -name = "wayland-commons" -version = "0.21.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40c08896768b667e1df195d88a62a53a2d1351a1ed96188be79c196b35bb32ec" -dependencies = [ - "nix 0.14.1", - "wayland-sys 0.21.13", + "wayland-commons", + "wayland-scanner", + "wayland-sys", ] [[package]] @@ -4843,20 +4740,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb66b0d1a27c39bbce712b6372131c6e25149f03ffb0cd017cf8f7de8d66dbdb" dependencies = [ "nix 0.14.1", - "wayland-sys 0.23.6", -] - -[[package]] -name = "wayland-protocols" -version = "0.21.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4afde2ea2a428eee6d7d2c8584fdbe8b82eee8b6c353e129a434cd6e07f42145" -dependencies = [ - "bitflags", - "wayland-client 0.21.13", - "wayland-commons 0.21.13", - "wayland-scanner 0.21.13", - "wayland-sys 0.21.13", + "wayland-sys", ] [[package]] @@ -4866,20 +4750,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6cc286643656742777d55dc8e70d144fa4699e426ca8e9d4ef454f4bf15ffcf9" dependencies = [ "bitflags", - "wayland-client 0.23.6", - "wayland-commons 0.23.6", - "wayland-scanner 0.23.6", -] - -[[package]] -name = "wayland-scanner" -version = "0.21.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf3828c568714507315ee425a9529edc4a4aa9901409e373e9e0027e7622b79e" -dependencies = [ - "proc-macro2 0.4.30", - "quote 0.6.13", - "xml-rs", + "wayland-client", + "wayland-commons", + "wayland-scanner", ] [[package]] @@ -4893,16 +4766,6 @@ dependencies = [ "xml-rs", ] -[[package]] -name = "wayland-sys" -version = "0.21.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520ab0fd578017a0ee2206623ba9ef4afe5e8f23ca7b42f6acfba2f4e66b1628" -dependencies = [ - "dlib", - "lazy_static", -] - [[package]] name = "wayland-sys" version = "0.23.6" @@ -4985,31 +4848,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "winit" -version = "0.19.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e96eb4bb472fa43e718e8fa4aef82f86cd9deac9483a1e1529230babdb394a8" -dependencies = [ - "android_glue", - "backtrace", - "bitflags", - "cocoa 0.18.5", - "core-foundation 0.6.4", - "core-graphics 0.17.3", - "lazy_static", - "libc", - "log", - "objc", - "parking_lot 0.9.0", - "percent-encoding 2.1.0", - "raw-window-handle", - "smithay-client-toolkit 0.4.6", - "wayland-client 0.21.13", - "winapi 0.3.8", - "x11-dl", -] - [[package]] name = "winit" version = "0.20.0" @@ -5018,7 +4856,7 @@ checksum = "3ba128780050481f453bec2a115b916dbc6ae79c303dee9bad8b9080bdccd4f5" dependencies = [ "android_glue", "bitflags", - "cocoa 0.19.1", + "cocoa", "core-foundation 0.6.4", "core-graphics 0.17.3", "core-video-sys", @@ -5034,8 +4872,8 @@ dependencies = [ "percent-encoding 2.1.0", "raw-window-handle", "serde", - "smithay-client-toolkit 0.6.6", - "wayland-client 0.23.6", + "smithay-client-toolkit", + "wayland-client", "winapi 0.3.8", "x11-dl", ] diff --git a/voxygen/Cargo.toml b/voxygen/Cargo.toml index 2e8e64ad7e..cc530f4513 100644 --- a/voxygen/Cargo.toml +++ b/voxygen/Cargo.toml @@ -25,7 +25,7 @@ anim = { package = "veloren-voxygen-anim", path = "src/anim", default-features = # Graphics gfx = "0.18.2" gfx_device_gl = { version = "0.16.2", optional = true } -gfx_window_glutin = "0.31.0" +gfx_window_glutin = {git = "https://github.com/Imberflur/gfx_window_glutin.git" } glutin = "0.22.0-alpha3" winit = { version = "0.20.0-alpha4", features = ["serde"] } conrod_core = { git = "https://gitlab.com/veloren/conrod.git", branch = "hide_text" } @@ -87,7 +87,7 @@ winres = "0.1" criterion = "0.3" git2 = "0.13" world = { package = "veloren-world", path = "../world" } -gfx_window_glutin = { version = "0.31.0", features = ["headless"] } +#gfx_window_glutin = { version = "0.31.0", features = ["headless"] } [[bench]] name = "meshing_benchmark" diff --git a/voxygen/src/window.rs b/voxygen/src/window.rs index e5f078253f..67bcf6f03c 100644 --- a/voxygen/src/window.rs +++ b/voxygen/src/window.rs @@ -508,7 +508,7 @@ impl Window { .with_vsync(false); let (window, device, factory, win_color_view, win_depth_view) = - gfx_window_glutin::init::( + gfx_window_glutin::init::( win_builder, ctx_builder, &events_loop, From d1b635efa4c3ce1cf192d08caf4007cc7c461d1a Mon Sep 17 00:00:00 2001 From: Imbris Date: Sun, 27 Oct 2019 03:11:18 -0400 Subject: [PATCH 45/85] Rearrange PlayState system to work without loop control --- Cargo.lock | 102 +++++- common/src/state.rs | 1 + voxygen/Cargo.toml | 5 +- voxygen/src/lib.rs | 28 +- voxygen/src/main.rs | 168 ++++------ voxygen/src/menu/char_selection/mod.rs | 91 ++--- voxygen/src/menu/main/mod.rs | 409 ++++++++++++----------- voxygen/src/run.rs | 142 ++++++++ voxygen/src/session.rs | 193 ++++++----- voxygen/src/ui/event.rs | 2 +- voxygen/src/window.rs | 438 +++++++++++++------------ 11 files changed, 897 insertions(+), 682 deletions(-) create mode 100644 voxygen/src/run.rs diff --git a/Cargo.lock b/Cargo.lock index b7635baa76..1c866300d8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -474,10 +474,11 @@ checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" [[package]] name = "cgl" -version = "0.3.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ced0551234e87afee12411d535648dd89d2e7f34c78b753395567aff3d447ff" +checksum = "55e7ec0b74fe5897894cbc207092c577e87c52f8a59e8ca8d97ef37551f60a49" dependencies = [ + "gleam", "libc", ] @@ -538,6 +539,21 @@ dependencies = [ "bitflags", ] +[[package]] +name = "cocoa" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1706996401131526e36b3b49f0c4d912639ce110996f3ca144d78946727bce54" +dependencies = [ + "bitflags", + "block", + "core-foundation 0.6.4", + "core-graphics 0.17.3", + "foreign-types", + "libc", + "objc", +] + [[package]] name = "cocoa" version = "0.19.1" @@ -1028,6 +1044,17 @@ dependencies = [ "byteorder 1.3.4", ] +[[package]] +name = "derivative" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c6d883546668a3e2011b6a716a7330b82eabb0151b138217f632c8243e17135" +dependencies = [ + "proc-macro2 0.4.30", + "quote 0.6.13", + "syn 0.15.44", +] + [[package]] name = "deunicode" version = "1.1.1" @@ -1656,6 +1683,15 @@ dependencies = [ "xml-rs", ] +[[package]] +name = "gleam" +version = "0.6.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cae10d7c99d0e77b4766e850a60898a17c1abaf01075531f1066f03dc7dc5fc5" +dependencies = [ + "gl_generator 0.13.1", +] + [[package]] name = "glib" version = "0.5.0" @@ -1698,15 +1734,16 @@ dependencies = [ [[package]] name = "glutin" -version = "0.22.1" +version = "0.22.0-alpha3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "639246c8838b02a83b9339bd87da3714e73e52ecfaa758e15d761eb77b2290b5" +checksum = "fcdd3592b515014281a21a42addd91f41eaff0c5f54295ee28dac8ea6bbbceba" dependencies = [ "android_glue", "cgl", - "cocoa", + "cocoa 0.18.5", "core-foundation 0.6.4", "core-graphics 0.17.3", + "derivative", "glutin_egl_sys", "glutin_emscripten_sys", "glutin_gles2_sys", @@ -1717,7 +1754,7 @@ dependencies = [ "log", "objc", "osmesa-sys", - "parking_lot 0.10.2", + "parking_lot 0.8.0", "wayland-client", "winapi 0.3.8", "winit", @@ -2270,6 +2307,15 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8dd5a6d5999d9907cda8ed67bbd137d3af8085216c2ac62de5be860bd41f304a" +[[package]] +name = "lock_api" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed946d4529956a20f2d63ebe1b69996d5a2137c91913fe3ebbeff957f5bca7ff" +dependencies = [ + "scopeguard", +] + [[package]] name = "lock_api" version = "0.3.4" @@ -2469,7 +2515,7 @@ name = "msgbox" version = "0.4.0" source = "git+https://github.com/bekker/msgbox-rs.git?rev=68fe39a#68fe39a60019b38a1569ae4e9ed796a0f0542673" dependencies = [ - "cocoa", + "cocoa 0.19.1", "gtk", "objc", "winapi 0.3.8", @@ -2804,13 +2850,24 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "parking_lot" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7767817701cce701d5585b9c4db3cdd02086398322c1d7e8bf5094a96a2ce7" +dependencies = [ + "lock_api 0.2.0", + "parking_lot_core 0.5.0", + "rustc_version", +] + [[package]] name = "parking_lot" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252" dependencies = [ - "lock_api", + "lock_api 0.3.4", "parking_lot_core 0.6.2", "rustc_version", ] @@ -2821,10 +2878,26 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3a704eb390aafdc107b0e392f56a82b668e3a71366993b5340f5833fd62505e" dependencies = [ - "lock_api", + "lock_api 0.3.4", "parking_lot_core 0.7.2", ] +[[package]] +name = "parking_lot_core" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb88cb1cb3790baa6776844f968fea3be44956cf184fa1be5a03341f5491278c" +dependencies = [ + "cfg-if", + "cloudabi", + "libc", + "rand 0.6.5", + "redox_syscall", + "rustc_version", + "smallvec 0.6.13", + "winapi 0.3.8", +] + [[package]] name = "parking_lot_core" version = "0.6.2" @@ -4850,13 +4923,14 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "winit" -version = "0.20.0" +version = "0.20.0-alpha4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ba128780050481f453bec2a115b916dbc6ae79c303dee9bad8b9080bdccd4f5" +checksum = "56c565622ccb05351d92445415952ca09dade6a53e75dd9e75d9bd35d4e99333" dependencies = [ "android_glue", "bitflags", - "cocoa", + "calloop", + "cocoa 0.19.1", "core-foundation 0.6.4", "core-graphics 0.17.3", "core-video-sys", @@ -4865,10 +4939,8 @@ dependencies = [ "lazy_static", "libc", "log", - "mio", - "mio-extras", "objc", - "parking_lot 0.10.2", + "parking_lot 0.9.0", "percent-encoding 2.1.0", "raw-window-handle", "serde", diff --git a/common/src/state.rs b/common/src/state.rs index edae2d01b0..64e8ef5511 100644 --- a/common/src/state.rs +++ b/common/src/state.rs @@ -255,6 +255,7 @@ impl State { .map(|(key, _)| key) .collect::>(); + // TODO: fix that this does nothing for key in keys { self.remove_chunk(key); } diff --git a/voxygen/Cargo.toml b/voxygen/Cargo.toml index cc530f4513..78d0f82045 100644 --- a/voxygen/Cargo.toml +++ b/voxygen/Cargo.toml @@ -26,8 +26,9 @@ anim = { package = "veloren-voxygen-anim", path = "src/anim", default-features = gfx = "0.18.2" gfx_device_gl = { version = "0.16.2", optional = true } gfx_window_glutin = {git = "https://github.com/Imberflur/gfx_window_glutin.git" } -glutin = "0.22.0-alpha3" -winit = { version = "0.20.0-alpha4", features = ["serde"] } +# TODO: change to non alpha +glutin = "=0.22.0-alpha3" +winit = { version = "=0.20.0-alpha4", features = ["serde"] } conrod_core = { git = "https://gitlab.com/veloren/conrod.git", branch = "hide_text" } conrod_winit = { git = "https://gitlab.com/veloren/conrod.git", branch = "hide_text" } euc = { git = "https://github.com/zesterer/euc.git" } diff --git a/voxygen/src/lib.rs b/voxygen/src/lib.rs index 02bc36d053..c821a23ad5 100644 --- a/voxygen/src/lib.rs +++ b/voxygen/src/lib.rs @@ -17,6 +17,7 @@ pub mod menu; pub mod mesh; pub mod profile; pub mod render; +pub mod run; pub mod scene; pub mod session; pub mod settings; @@ -28,9 +29,14 @@ pub mod window; pub use crate::error::Error; use crate::{ - audio::AudioFrontend, profile::Profile, settings::Settings, singleplayer::Singleplayer, - window::Window, + audio::AudioFrontend, + profile::Profile, + render::Renderer, + settings::Settings, + singleplayer::Singleplayer, + window::{Event, Window}, }; +use common::{assets::watch, clock::Clock}; /// A type used to store state that is shared between all play states. pub struct GlobalState { @@ -39,7 +45,11 @@ pub struct GlobalState { pub window: Window, pub audio: AudioFrontend, pub info_message: Option, + pub clock: Clock, + #[cfg(feature = "singleplayer")] pub singleplayer: Option, + // TODO: redo this so that the watcher doesn't have to exist for reloading to occur + localization_watcher: watch::ReloadIndicator, } impl GlobalState { @@ -61,6 +71,8 @@ pub enum Direction { /// States can either close (and revert to a previous state), push a new state /// on top of themselves, or switch to a totally different state. pub enum PlayStateResult { + /// Keep running this play state. + Continue, /// Pop all play states in reverse order and shut down the program. Shutdown, /// Close the current play state and pop it from the play state stack. @@ -74,10 +86,16 @@ pub enum PlayStateResult { /// A trait representing a playable game state. This may be a menu, a game /// session, the title screen, etc. pub trait PlayState { - /// Play the state until some change of state is required (i.e: a menu is - /// opened or the game is closed). - fn play(&mut self, direction: Direction, global_state: &mut GlobalState) -> PlayStateResult; + /// Get a descriptive name for this state type. + /// Called when entering this play state from another + fn enter(&mut self, global_state: &mut GlobalState, direction: Direction); + + /// Tick the play state + fn tick(&mut self, global_state: &mut GlobalState, events: Vec) -> PlayStateResult; /// Get a descriptive name for this state type. fn name(&self) -> &'static str; + + /// Draw the play state. + fn render(&mut self, renderer: &mut Renderer, settings: &Settings); } diff --git a/voxygen/src/main.rs b/voxygen/src/main.rs index b4f64dc017..4adf66812f 100644 --- a/voxygen/src/main.rs +++ b/voxygen/src/main.rs @@ -14,7 +14,10 @@ use veloren_voxygen::{ Direction, GlobalState, PlayState, PlayStateResult, }; -use common::assets::{load, load_expect}; +use common::{ + assets::{load_watched, watch}, + clock::Clock, +}; use std::{mem, panic}; use tracing::{debug, error, warn}; @@ -26,57 +29,14 @@ fn main() { // Note: This won't log anything due to it being called before // `logging::init`. The issue is we need to read a setting to decide // whether we create a log file or not. - let settings = Settings::load(); - - // Init logging and hold the guards. - let _guards = logging::init(&settings); - - // Save settings to add new fields or create the file if it is not already - // there. + let mut settings = Settings::load(); + // Save settings to add new fields or create the file if it is not already there if let Err(err) = settings.save_to_file() { panic!("Failed to save settings: {:?}", err); } - let mut audio = match settings.audio.output { - AudioOutput::Off => None, - AudioOutput::Automatic => audio::get_default_device(), - AudioOutput::Device(ref dev) => Some(dev.clone()), - } - .map(|dev| AudioFrontend::new(dev, settings.audio.max_sfx_channels)) - .unwrap_or_else(AudioFrontend::no_audio); - - audio.set_music_volume(settings.audio.music_volume); - audio.set_sfx_volume(settings.audio.sfx_volume); - - // Load the profile. - let profile = Profile::load(); - - let mut global_state = GlobalState { - audio, - profile, - window: Window::new(&settings).expect("Failed to create window!"), - settings, - info_message: None, - singleplayer: None, - }; - - // Try to load the localization and log missing entries - let localized_strings = load::(&i18n_asset_key( - &global_state.settings.language.selected_language, - )) - .unwrap_or_else(|e| { - let preferred_language = &global_state.settings.language.selected_language; - warn!( - ?e, - ?preferred_language, - "Impossible to load language: change to the default language (English) instead.", - ); - global_state.settings.language.selected_language = i18n::REFERENCE_LANG.to_owned(); - load_expect::(&i18n_asset_key( - &global_state.settings.language.selected_language, - )) - }); - localized_strings.log_missing_entries(); + // Init logging and hold the guards. + let _guards = logging::init(&settings); // Set up panic handler to relay swish panic messages to the user let default_hook = panic::take_hook(); @@ -159,68 +119,56 @@ fn main() { #[cfg(feature = "hot-anim")] anim::init(); - // Set up the initial play state. - let mut states: Vec> = vec![Box::new(MainMenuState::new(&mut global_state))]; - states.last().map(|current_state| { - let current_state = current_state.name(); - debug!(?current_state, "Started game with state") - }); - - // What's going on here? - // --------------------- - // The state system used by Voxygen allows for the easy development of - // stack-based menus. For example, you may want a "title" state that can - // push a "main menu" state on top of it, which can in turn push a - // "settings" state or a "game session" state on top of it. The code below - // manages the state transfer logic automatically so that we don't have to - // re-engineer it for each menu we decide to add to the game. - let mut direction = Direction::Forwards; - while let Some(state_result) = states - .last_mut() - .map(|last| last.play(direction, &mut global_state)) - { - // Implement state transfer logic. - match state_result { - PlayStateResult::Shutdown => { - direction = Direction::Backwards; - debug!("Shutting down all states..."); - while states.last().is_some() { - states.pop().map(|old_state| { - let old_state = old_state.name(); - debug!(?old_state, "Popped state"); - global_state.on_play_state_changed(); - }); - } - }, - PlayStateResult::Pop => { - direction = Direction::Backwards; - states.pop().map(|old_state| { - let old_state = old_state.name(); - debug!(?old_state, "Popped state"); - global_state.on_play_state_changed(); - }); - }, - PlayStateResult::Push(new_state) => { - direction = Direction::Forwards; - debug!("Pushed state '{}'.", new_state.name()); - states.push(new_state); - global_state.on_play_state_changed(); - }, - PlayStateResult::Switch(mut new_state_box) => { - direction = Direction::Forwards; - states.last_mut().map(|old_state_box| { - let old_state = old_state_box.name(); - let new_state = new_state_box.name(); - debug!(?old_state, ?new_state, "Switching to states",); - mem::swap(old_state_box, &mut new_state_box); - global_state.on_play_state_changed(); - }); - }, - } + // Setup audio + let mut audio = match settings.audio.output { + AudioOutput::Off => None, + AudioOutput::Automatic => audio::get_default_device(), + AudioOutput::Device(ref dev) => Some(dev.clone()), } + .map(|dev| AudioFrontend::new(dev, settings.audio.max_sfx_channels)) + .unwrap_or_else(AudioFrontend::no_audio); - // Save any unsaved changes to profile. - global_state.profile.save_to_file_warn(); - // Save any unsaved changes to settings. - global_state.settings.save_to_file_warn(); + audio.set_music_volume(settings.audio.music_volume); + audio.set_sfx_volume(settings.audio.sfx_volume); + + // Load the profile. + let profile = Profile::load(); + + let mut localization_watcher = watch::ReloadIndicator::new(); + let localized_strings = load_watched::( + &i18n_asset_key(&settings.language.selected_language), + &mut localization_watcher, + ) + .unwrap_or_else(|error| { + let preferred_language = &global_state.settings.language.selected_language; + warn!( + ?e, + ?preferred_language, + "Impossible to load language: change to the default language (English) instead.", + ); + settings.language.selected_language = i18n::REFERENCE_LANG.to_owned(); + load_watched::( + &i18n_asset_key(&settings.language.selected_language), + &mut localization_watcher, + ) + .unwrap() + }); + localized_strings.log_missing_entries(); + + // Create window + let (window, event_loop) = Window::new(&settings).expect("Failed to create window!"); + + let global_state = GlobalState { + audio, + profile, + window, + settings, + clock: Clock::start(), + info_message: None, + #[cfg(feature = "singleplayer")] + singleplayer: None, + localization_watcher, + }; + + run::run(global_state, event_loop); } diff --git a/voxygen/src/menu/char_selection/mod.rs b/voxygen/src/menu/char_selection/mod.rs index bb8962d6f8..3ed4305be5 100644 --- a/voxygen/src/menu/char_selection/mod.rs +++ b/voxygen/src/menu/char_selection/mod.rs @@ -2,15 +2,17 @@ mod ui; use crate::{ i18n::{i18n_asset_key, VoxygenLocalization}, + render::Renderer, scene::simple::{self as scene, Scene}, session::SessionState, + settings::Settings, window::Event as WinEvent, Direction, GlobalState, PlayState, PlayStateResult, }; use client::{self, Client}; -use common::{assets, clock::Clock, comp, msg::ClientState, state::DeltaTime}; +use common::{assets, comp, msg::ClientState, state::DeltaTime}; use specs::WorldExt; -use std::{cell::RefCell, rc::Rc, time::Duration}; +use std::{cell::RefCell, rc::Rc}; use tracing::error; use ui::CharSelectionUi; @@ -32,20 +34,34 @@ impl CharSelectionState { ), } } + + fn get_humanoid_body(&self) -> Option { + self.char_selection_ui + .get_character_list() + .and_then(|data| { + if let Some(character) = data.get(self.char_selection_ui.selected_character) { + match character.body { + comp::Body::Humanoid(body) => Some(body), + _ => None, + } + } else { + None + } + }) + } } impl PlayState for CharSelectionState { - fn play(&mut self, _: Direction, global_state: &mut GlobalState) -> PlayStateResult { - // Set up an fps clock. - let mut clock = Clock::start(); - + fn enter(&mut self, _: &mut GlobalState, _: Direction) { // Load the player's character list self.client.borrow_mut().load_character_list(); + } - let mut current_client_state = self.client.borrow().get_client_state(); - while let ClientState::Pending | ClientState::Registered = current_client_state { + fn tick(&mut self, global_state: &mut GlobalState, events: Vec) -> PlayStateResult { + let client_state = self.client.borrow().get_client_state(); + if let ClientState::Pending | ClientState::Registered = client_state { // Handle window events - for event in global_state.window.fetch_events(&mut global_state.settings) { + for event in events { if self.char_selection_ui.handle_event(event.clone()) { continue; } @@ -60,8 +76,6 @@ impl PlayState for CharSelectionState { } } - global_state.window.renderer_mut().clear(); - // Maintain the UI. let events = self .char_selection_ui @@ -100,9 +114,6 @@ impl PlayState for CharSelectionState { } } - // Maintain global state. - global_state.maintain(clock.get_last_delta().as_secs_f32()); - let humanoid_body = self .char_selection_ui .get_character_list() @@ -117,6 +128,7 @@ impl PlayState for CharSelectionState { } }); + let humanoid_body = self.get_humanoid_body(); let loadout = self.char_selection_ui.get_loadout(); // Maintain the scene. @@ -141,17 +153,6 @@ impl PlayState for CharSelectionState { loadout.as_ref(), ); } - // Render the scene. - self.scene.render( - global_state.window.renderer_mut(), - self.client.borrow().get_tick(), - humanoid_body, - loadout.as_ref(), - ); - - // Draw the UI to the screen. - self.char_selection_ui - .render(global_state.window.renderer_mut(), self.scene.globals()); // Tick the client (currently only to keep the connection alive). let localized_strings = assets::load_expect::(&i18n_asset_key( @@ -160,7 +161,7 @@ impl PlayState for CharSelectionState { match self.client.borrow_mut().tick( comp::ControllerInputs::default(), - clock.get_last_delta(), + global_state.clock.get_last_delta(), |_| {}, ) { Ok(events) => { @@ -190,25 +191,33 @@ impl PlayState for CharSelectionState { }, } + // TODO: make sure rendering is not relying on cleaned up stuff self.client.borrow_mut().cleanup(); - // Finish the frame. - global_state.window.renderer_mut().flush(); - global_state - .window - .swap_buffers() - .expect("Failed to swap window buffers"); - - // Wait for the next tick. - clock.tick(Duration::from_millis( - 1000 / (global_state.settings.graphics.max_fps as u64), - )); - - current_client_state = self.client.borrow().get_client_state(); + PlayStateResult::Continue + } else { + error!("Client not in pending or registered state. Popping char selection play state"); + // TODO set global_state.info_message + PlayStateResult::Pop } - - PlayStateResult::Pop } fn name(&self) -> &'static str { "Title" } + + fn render(&mut self, renderer: &mut Renderer, _: &Settings) { + let humanoid_body = self.get_humanoid_body(); + let loadout = self.char_selection_ui.get_loadout(); + + // Render the scene. + self.scene.render( + renderer, + self.client.borrow().get_tick(), + humanoid_body, + loadout.as_ref(), + ); + + // Draw the UI to the screen. + self.char_selection_ui + .render(renderer, self.scene.globals()); + } } diff --git a/voxygen/src/menu/main/mod.rs b/voxygen/src/menu/main/mod.rs index cdd9410545..e7465f5fec 100644 --- a/voxygen/src/menu/main/mod.rs +++ b/voxygen/src/menu/main/mod.rs @@ -1,19 +1,25 @@ mod client_init; -#[cfg(feature = "singleplayer")] mod ui; +mod ui; +// TODO: make sure turning off singleplayer feature still works (also make sure +// all singleplayer stuff is still hidden with it off) use super::char_selection::CharSelectionState; +#[cfg(feature = "singleplayer")] +use crate::singleplayer::Singleplayer; use crate::{ - singleplayer::Singleplayer, window::Event, Direction, GlobalState, PlayState, PlayStateResult, + render::Renderer, settings::Settings, window::Event, Direction, GlobalState, PlayState, + PlayStateResult, }; use client_init::{ClientInit, Error as InitError, Msg as InitMsg}; -use common::{assets::load_expect, clock::Clock, comp}; -#[cfg(feature = "singleplayer")] -use std::time::Duration; +use common::{assets::load_expect, comp}; use tracing::{error, warn}; use ui::{Event as MainMenuEvent, MainMenuUi}; pub struct MainMenuState { main_menu_ui: MainMenuUi, + title_music_channel: Option, + // Used for client creation. + client_init: Option, } impl MainMenuState { @@ -21,6 +27,8 @@ impl MainMenuState { pub fn new(global_state: &mut GlobalState) -> Self { Self { main_menu_ui: MainMenuUi::new(global_state), + title_music_channel: None, + client_init: None, } } } @@ -28,237 +36,224 @@ impl MainMenuState { const DEFAULT_PORT: u16 = 14004; impl PlayState for MainMenuState { - #[allow(clippy::useless_format)] // TODO: Pending review in #587 - fn play(&mut self, _: Direction, global_state: &mut GlobalState) -> PlayStateResult { - // Set up an fps clock. - let mut clock = Clock::start(); - - // Used for client creation. - let mut client_init: Option = None; - + fn enter(&mut self, global_state: &mut GlobalState, _: Direction) { // Kick off title music if global_state.settings.audio.output.is_enabled() && global_state.audio.music_enabled() { global_state.audio.play_title_music(); } // Reset singleplayer server if it was running already - global_state.singleplayer = None; + #[cfg(feature = "singleplayer")] + { + global_state.singleplayer = None; + } + } + fn tick(&mut self, global_state: &mut GlobalState, events: Vec) -> PlayStateResult { let localized_strings = load_expect::( &crate::i18n::i18n_asset_key(&global_state.settings.language.selected_language), ); - loop { - // Handle window events. - for event in global_state.window.fetch_events(&mut global_state.settings) { - match event { - Event::Close => return PlayStateResult::Shutdown, - // Pass events to ui. - Event::Ui(event) => { - self.main_menu_ui.handle_event(event); - }, - // Ignore all other events. - _ => {}, - } - } - - global_state.window.renderer_mut().clear(); - - // Poll client creation. - match client_init.as_ref().and_then(|init| init.poll()) { - Some(InitMsg::Done(Ok(mut client))) => { - self.main_menu_ui.connected(); - // Register voxygen components / resources - crate::ecs::init(client.state_mut().ecs_mut()); - return PlayStateResult::Push(Box::new(CharSelectionState::new( - global_state, - std::rc::Rc::new(std::cell::RefCell::new(client)), - ))); + // Handle window events. + for event in events { + match event { + Event::Close => return PlayStateResult::Shutdown, + // Pass events to ui. + Event::Ui(event) => { + self.main_menu_ui.handle_event(event); }, - Some(InitMsg::Done(Err(e))) => { - client_init = None; - global_state.info_message = Some({ - let err = match e { - InitError::BadAddress(_) | InitError::NoAddress => { - localized_strings.get("main.login.server_not_found").into() + // Ignore all other events. + _ => {}, + } + } + // Poll client creation. + match self.client_init.as_ref().and_then(|init| init.poll()) { + Some(InitMsg::Done(Ok(mut client))) => { + self.main_menu_ui.connected(); + // Register voxygen components / resources + crate::ecs::init(client.state_mut().ecs_mut()); + return PlayStateResult::Push(Box::new(CharSelectionState::new( + global_state, + std::rc::Rc::new(std::cell::RefCell::new(client)), + ))); + }, + Some(InitMsg::Done(Err(err))) => { + self.client_init = None; + global_state.info_message = Some({ + let err = match err { + InitError::BadAddress(_) | InitError::NoAddress => { + localized_strings.get("main.login.server_not_found").into() + }, + InitError::ClientError(err) => match err { + client::Error::AuthErr(e) => format!( + "{}: {}", + localized_strings.get("main.login.authentication_error"), + e + ), + client::Error::TooManyPlayers => { + localized_strings.get("main.login.server_full").into() }, - InitError::ClientError(err) => match err { - client::Error::AuthErr(e) => format!( + client::Error::AuthServerNotTrusted => localized_strings + .get("main.login.untrusted_auth_server") + .into(), + client::Error::ServerWentMad => localized_strings + .get("main.login.outdated_client_or_server") + .into(), + client::Error::ServerTimeout => { + localized_strings.get("main.login.timeout").into() + }, + client::Error::ServerShutdown => { + localized_strings.get("main.login.server_shut_down").into() + }, + client::Error::AlreadyLoggedIn => { + localized_strings.get("main.login.already_logged_in").into() + }, + client::Error::NotOnWhitelist => { + localized_strings.get("main.login.not_on_whitelist").into() + }, + client::Error::InvalidCharacter => { + localized_strings.get("main.login.invalid_character").into() + }, + client::Error::NetworkErr(e) => format!( + "{}: {:?}", + localized_strings.get("main.login.network_error"), + e + ), + client::Error::ParticipantErr(e) => format!( + "{}: {:?}", + localized_strings.get("main.login.network_error"), + e + ), + client::Error::StreamErr(e) => format!( + "{}: {:?}", + localized_strings.get("main.login.network_error"), + e + ), + client::Error::Other(e) => { + format!("{}: {}", localized_strings.get("common.error"), e) + }, + client::Error::AuthClientError(e) => match e { + client::AuthClientError::JsonError(e) => format!( "{}: {}", - localized_strings.get("main.login.authentication_error"), + localized_strings.get("common.fatal_error"), e ), - client::Error::TooManyPlayers => { - localized_strings.get("main.login.server_full").into() - }, - client::Error::AuthServerNotTrusted => localized_strings - .get("main.login.untrusted_auth_server") - .into(), - client::Error::ServerWentMad => localized_strings - .get("main.login.outdated_client_or_server") - .into(), - client::Error::ServerTimeout => { - localized_strings.get("main.login.timeout").into() - }, - client::Error::ServerShutdown => { - localized_strings.get("main.login.server_shut_down").into() - }, - client::Error::AlreadyLoggedIn => { - localized_strings.get("main.login.already_logged_in").into() - }, - client::Error::NotOnWhitelist => { - localized_strings.get("main.login.not_on_whitelist").into() - }, - client::Error::NetworkErr(e) => format!( - "{}: {:?}", - localized_strings.get("main.login.network_error"), - e + // TODO: remove parentheses + client::AuthClientError::RequestError() => format!( + "{}", + localized_strings.get("main.login.failed_sending_request"), ), - client::Error::ParticipantErr(e) => format!( - "{}: {:?}", - localized_strings.get("main.login.network_error"), - e - ), - client::Error::StreamErr(e) => format!( - "{}: {:?}", - localized_strings.get("main.login.network_error"), - e - ), - client::Error::Other(e) => { - format!("{}: {}", localized_strings.get("common.error"), e) - }, - client::Error::AuthClientError(e) => match e { - client::AuthClientError::JsonError(e) => format!( - "{}: {}", - localized_strings.get("common.fatal_error"), - e - ), - client::AuthClientError::RequestError() => format!( - "{}", - localized_strings.get("main.login.failed_sending_request") - ), - client::AuthClientError::ServerError(_, e) => format!("{}", e), - }, - client::Error::InvalidCharacter => { - localized_strings.get("main.login.invalid_character").into() - }, + client::AuthClientError::ServerError(_, e) => format!("{}", e), }, - InitError::ClientCrashed => { - localized_strings.get("main.login.client_crashed").into() - }, - }; - // Log error for possible additional use later or incase that the error - // displayed is cut of. - error!("{}", err); - err - }); - }, - Some(InitMsg::IsAuthTrusted(auth_server)) => { - if global_state - .settings - .networking - .trusted_auth_servers - .contains(&auth_server) - { - // Can't fail since we just polled it, it must be Some - client_init.as_ref().unwrap().auth_trust(auth_server, true); - } else { - // Show warning that auth server is not trusted and prompt for approval - self.main_menu_ui.auth_trust_prompt(auth_server); - } - }, - None => {}, - } + }, + InitError::ClientCrashed => { + localized_strings.get("main.login.client_crashed").into() + }, + }; + // Log error for possible additional use later or incase that the error + // displayed is cut of. + error!("{}", err); + err + }); + }, + Some(InitMsg::IsAuthTrusted(auth_server)) => { + if global_state + .settings + .networking + .trusted_auth_servers + .contains(&auth_server) + { + // Can't fail since we just polled it, it must be Some + self.client_init + .as_ref() + .unwrap() + .auth_trust(auth_server, true); + } else { + // Show warning that auth server is not trusted and prompt for approval + self.main_menu_ui.auth_trust_prompt(auth_server); + } + }, + None => {}, + } - // Maintain global_state - global_state.maintain(clock.get_last_delta().as_secs_f32()); - - // Maintain the UI. - for event in self - .main_menu_ui - .maintain(global_state, clock.get_last_delta()) - { - match event { - MainMenuEvent::LoginAttempt { + // Maintain the UI. + for event in self + .main_menu_ui + .maintain(global_state, global_state.clock.get_last_delta()) + { + match event { + MainMenuEvent::LoginAttempt { + username, + password, + server_address, + } => { + attempt_login( + global_state, username, password, server_address, - } => { - attempt_login( - global_state, - username, - password, - server_address, - DEFAULT_PORT, - &mut client_init, - ); - }, - MainMenuEvent::CancelLoginAttempt => { - // client_init contains Some(ClientInit), which spawns a thread which - // contains a TcpStream::connect() call This call is - // blocking TODO fix when the network rework happens - global_state.singleplayer = None; - client_init = None; - self.main_menu_ui.cancel_connection(); - }, + DEFAULT_PORT, + &mut self.client_init, + ); + }, + MainMenuEvent::CancelLoginAttempt => { + // client_init contains Some(ClientInit), which spawns a thread which contains a + // TcpStream::connect() call This call is blocking + // TODO fix when the network rework happens #[cfg(feature = "singleplayer")] - MainMenuEvent::StartSingleplayer => { - let (singleplayer, server_settings) = Singleplayer::new(None); // TODO: Make client and server use the same thread pool + { + global_state.singleplayer = None; + } + self.client_init = None; + self.main_menu_ui.cancel_connection(); + }, + #[cfg(feature = "singleplayer")] + MainMenuEvent::StartSingleplayer => { + let (singleplayer, server_settings) = Singleplayer::new(None); // TODO: Make client and server use the same thread pool - global_state.singleplayer = Some(singleplayer); - - attempt_login( - global_state, - "singleplayer".to_owned(), - "".to_owned(), - server_settings.gameserver_address.ip().to_string(), - server_settings.gameserver_address.port(), - &mut client_init, - ); - }, - MainMenuEvent::Settings => {}, // TODO - MainMenuEvent::Quit => return PlayStateResult::Shutdown, - /*MainMenuEvent::DisclaimerClosed => { - global_state.settings.show_disclaimer = false - },*/ - MainMenuEvent::AuthServerTrust(auth_server, trust) => { - if trust { - global_state - .settings - .networking - .trusted_auth_servers - .insert(auth_server.clone()); - global_state.settings.save_to_file_warn(); - } - client_init - .as_ref() - .map(|init| init.auth_trust(auth_server, trust)); - }, - } + attempt_login( + global_state, + "singleplayer".to_owned(), + "".to_owned(), + server_settings.gameserver_address.ip().to_string(), + server_settings.gameserver_address.port(), + &mut self.client_init, + ); + }, + MainMenuEvent::Settings => {}, // TODO + MainMenuEvent::Quit => return PlayStateResult::Shutdown, + /*MainMenuEvent::DisclaimerClosed => { + global_state.settings.show_disclaimer = false + },*/ + MainMenuEvent::AuthServerTrust(auth_server, trust) => { + if trust { + global_state + .settings + .networking + .trusted_auth_servers + .insert(auth_server.clone()); + global_state.settings.save_to_file_warn(); + } + self.client_init + .as_ref() + .map(|init| init.auth_trust(auth_server, trust)); + }, } - - if let Some(info) = global_state.info_message.take() { - self.main_menu_ui.show_info(info); - } - - // Draw the UI to the screen. - self.main_menu_ui.render(global_state.window.renderer_mut()); - - // Finish the frame. - global_state.window.renderer_mut().flush(); - global_state - .window - .swap_buffers() - .expect("Failed to swap window buffers!"); - - // Wait for the next tick - clock.tick(Duration::from_millis( - 1000 / (global_state.settings.graphics.max_fps as u64), - )); } + + if let Some(info) = global_state.info_message.take() { + self.main_menu_ui.show_info(info); + } + + PlayStateResult::Continue } fn name(&self) -> &'static str { "Title" } + + fn render(&mut self, renderer: &mut Renderer, _: &Settings) { + // Draw the UI to the screen. + self.main_menu_ui.render(renderer); + } } fn attempt_login( diff --git a/voxygen/src/run.rs b/voxygen/src/run.rs new file mode 100644 index 0000000000..bc43253928 --- /dev/null +++ b/voxygen/src/run.rs @@ -0,0 +1,142 @@ +use crate::{ + menu::main::MainMenuState, + ui, + window::{Event, EventLoop}, + Direction, GlobalState, PlayState, PlayStateResult, +}; +use std::{mem, time::Duration}; +use tracing::debug; + +pub fn run(mut global_state: GlobalState, event_loop: EventLoop) { + // Set up the initial play state. + let mut states: Vec> = vec![Box::new(MainMenuState::new(&mut global_state))]; + states.last_mut().map(|current_state| { + current_state.enter(&mut global_state, Direction::Forwards); + let current_state = current_state.name(); + debug!(?current_state, "Started game with state"); + }); + + event_loop.run(move |event, _, control_flow| { + // Get events for the ui. + if let Some(event) = ui::Event::try_from(event.clone(), global_state.window.window()) { + global_state.window.send_event(Event::Ui(event)); + } + + match event { + winit::event::Event::EventsCleared => { + // Run tick here + + // What's going on here? + // --------------------- + // The state system used by Voxygen allows for the easy development of + // stack-based menus. For example, you may want a "title" state + // that can push a "main menu" state on top of it, which can in + // turn push a "settings" state or a "game session" state on top of it. + // The code below manages the state transfer logic automatically so that we + // don't have to re-engineer it for each menu we decide to add + // to the game. + let mut exit = true; + while let Some(state_result) = states.last_mut().map(|last| { + let events = global_state.window.fetch_events(); + last.tick(&mut global_state, events) + }) { + // Implement state transfer logic. + match state_result { + PlayStateResult::Continue => { + // Wait for the next tick. + global_state.clock.tick(Duration::from_millis( + 1000 / global_state.settings.graphics.max_fps as u64, + )); + + // Maintain global state. + global_state + .maintain(global_state.clock.get_last_delta().as_secs_f32()); + + exit = false; + break; + }, + PlayStateResult::Shutdown => { + debug!("Shutting down all states..."); + while states.last().is_some() { + states.pop().map(|old_state| { + debug!("Popped state '{}'.", old_state.name()); + global_state.on_play_state_changed(); + }); + } + }, + PlayStateResult::Pop => { + states.pop().map(|old_state| { + debug!("Popped state '{}'.", old_state.name()); + global_state.on_play_state_changed(); + }); + states.last_mut().map(|new_state| { + new_state.enter(&mut global_state, Direction::Forwards); + }); + }, + PlayStateResult::Push(mut new_state) => { + new_state.enter(&mut global_state, Direction::Forwards); + debug!("Pushed state '{}'.", new_state.name()); + states.push(new_state); + global_state.on_play_state_changed(); + }, + PlayStateResult::Switch(mut new_state) => { + new_state.enter(&mut global_state, Direction::Forwards); + states.last_mut().map(|old_state| { + debug!( + "Switching to state '{}' from state '{}'.", + new_state.name(), + old_state.name() + ); + mem::swap(old_state, &mut new_state); + global_state.on_play_state_changed(); + }); + }, + } + } + + *control_flow = if exit { + winit::event_loop::ControlFlow::Exit + } else { + winit::event_loop::ControlFlow::Poll + }; + + // TODO: move + if let Some(last) = states.last_mut() { + global_state.window.renderer_mut().clear(); + last.render( + &mut global_state.window.renderer_mut(), + &global_state.settings, + ); + // Finish the frame. + global_state.window.renderer_mut().flush(); + global_state + .window + .swap_buffers() + .expect("Failed to swap window buffers!"); + } + //global_state.window.request_redraw(); + }, + /*winit::event::Event::WindowEvent { + event: WindowEvent::RedrawRequested, + .. + } => { + // render here + } + .. + *control_flow = ControlFlow::Exit; + }*/ + winit::event::Event::WindowEvent { event, .. } => global_state + .window + .handle_window_event(event, &mut global_state.settings), + winit::event::Event::DeviceEvent { event, .. } => { + global_state.window.handle_device_event(event) + }, + winit::event::Event::LoopDestroyed => { + // Save any unsaved changes to settings and profile + global_state.settings.save_to_file_warn(); + global_state.profile.save_to_file_warn(); + }, + _ => {}, + } + }); +} diff --git a/voxygen/src/session.rs b/voxygen/src/session.rs index d6d1b48148..668fb11901 100644 --- a/voxygen/src/session.rs +++ b/voxygen/src/session.rs @@ -5,15 +5,15 @@ use crate::{ i18n::{i18n_asset_key, VoxygenLocalization}, key_state::KeyState, menu::char_selection::CharSelectionState, + render::Renderer, scene::{camera, Scene, SceneData}, - settings::{AudioOutput, ControlSettings}, + settings::{AudioOutput, ControlSettings, Settings}, window::{AnalogGameInput, Event, GameInput}, Direction, Error, GlobalState, PlayState, PlayStateResult, }; use client::{self, Client}; use common::{ - assets::{load_expect, load_watched, watch}, - clock::Clock, + assets::{load_expect, load_watched}, comp, comp::{ ChatMsg, ChatType, InventoryUpdateEvent, Pos, Vel, MAX_MOUNT_RANGE_SQR, @@ -46,6 +46,12 @@ pub struct SessionState { inputs: comp::ControllerInputs, selected_block: Block, voxygen_i18n: std::sync::Arc, + walk_forward_dir: Vec2, + walk_right_dir: Vec2, + freefly_vel: Vec3, + free_look: bool, + auto_walk: bool, + is_aiming: bool, } /// Represents an active game session (i.e., the one being played). @@ -67,6 +73,9 @@ impl SessionState { let voxygen_i18n = load_expect::(&i18n_asset_key( &global_state.settings.language.selected_language, )); + + let walk_forward_dir = scene.camera().forward_xy(); + let walk_right_dir = scene.camera().right_xy(); Self { scene, client, @@ -75,6 +84,12 @@ impl SessionState { hud, selected_block: Block::new(BlockKind::Normal, Rgb::broadcast(255)), voxygen_i18n, + walk_forward_dir, + walk_right_dir, + freefly_vel: Vec3::zero(), + free_look: false, + auto_walk: false, + is_aiming: false, } } } @@ -153,12 +168,10 @@ impl SessionState { } impl PlayState for SessionState { - fn play(&mut self, _: Direction, global_state: &mut GlobalState) -> PlayStateResult { + fn enter(&mut self, global_state: &mut GlobalState, _: Direction) { // Trap the cursor. global_state.window.grab_cursor(true); - // Set up an fps clock. - let mut clock = Clock::start(); self.client.borrow_mut().clear_terrain(); // Send startup commands to the server @@ -167,30 +180,22 @@ impl PlayState for SessionState { self.client.borrow_mut().send_chat(cmd.to_string()); } } + } - // Keep a watcher on the language - let mut localization_watcher = watch::ReloadIndicator::new(); - let mut localized_strings = load_watched::( - &i18n_asset_key(&global_state.settings.language.selected_language), - &mut localization_watcher, - ) - .unwrap(); - - let mut walk_forward_dir = self.scene.camera().forward_xy(); - let mut walk_right_dir = self.scene.camera().right_xy(); - let mut freefly_vel = Vec3::zero(); - let mut free_look = false; - let mut auto_walk = false; + fn tick(&mut self, global_state: &mut GlobalState, events: Vec) -> PlayStateResult { + let localized_strings = load_expect::(&i18n_asset_key( + &global_state.settings.language.selected_language, + )); + // TODO: can this be a method on the session or are there borrowcheck issues? fn stop_auto_walk(auto_walk: &mut bool, key_state: &mut KeyState, hud: &mut Hud) { *auto_walk = false; hud.auto_walk(false); key_state.auto_walk = false; } - // Game loop - let mut current_client_state = self.client.borrow().get_client_state(); - while let ClientState::Pending | ClientState::Character = current_client_state { + let client_state = self.client.borrow().get_client_state(); + if let ClientState::Pending | ClientState::Character = client_state { // Compute camera data self.scene .camera_mut() @@ -229,6 +234,7 @@ impl PlayState for SessionState { }, ) }; + self.is_aiming = is_aiming; let cam_dir: Vec3 = Vec3::from(view_mat.inverted() * -Vec4::unit_z()); @@ -278,7 +284,7 @@ impl PlayState for SessionState { })); // Handle window events. - for event in global_state.window.fetch_events(&mut global_state.settings) { + for event in events { // Pass all events to the ui first. if self.hud.handle_event(event.clone(), global_state) { continue; @@ -348,7 +354,7 @@ impl PlayState for SessionState { { self.key_state.toggle_sit = state; if state { - stop_auto_walk(&mut auto_walk, &mut self.key_state, &mut self.hud); + stop_auto_walk(&mut self.auto_walk, &mut self.key_state, &mut self.hud); self.client.borrow_mut().toggle_sit(); } } @@ -357,31 +363,31 @@ impl PlayState for SessionState { { self.key_state.toggle_dance = state; if state { - stop_auto_walk(&mut auto_walk, &mut self.key_state, &mut self.hud); + stop_auto_walk(&mut self.auto_walk, &mut self.key_state, &mut self.hud); self.client.borrow_mut().toggle_dance(); } } Event::InputUpdate(GameInput::MoveForward, state) => { if state && global_state.settings.gameplay.stop_auto_walk_on_input { - stop_auto_walk(&mut auto_walk, &mut self.key_state, &mut self.hud); + stop_auto_walk(&mut self.auto_walk, &mut self.key_state, &mut self.hud); } self.key_state.up = state }, Event::InputUpdate(GameInput::MoveBack, state) => { if state && global_state.settings.gameplay.stop_auto_walk_on_input { - stop_auto_walk(&mut auto_walk, &mut self.key_state, &mut self.hud); + stop_auto_walk(&mut self.auto_walk, &mut self.key_state, &mut self.hud); } self.key_state.down = state }, Event::InputUpdate(GameInput::MoveLeft, state) => { if state && global_state.settings.gameplay.stop_auto_walk_on_input { - stop_auto_walk(&mut auto_walk, &mut self.key_state, &mut self.hud); + stop_auto_walk(&mut self.auto_walk, &mut self.key_state, &mut self.hud); } self.key_state.left = state }, Event::InputUpdate(GameInput::MoveRight, state) => { if state && global_state.settings.gameplay.stop_auto_walk_on_input { - stop_auto_walk(&mut auto_walk, &mut self.key_state, &mut self.hud); + stop_auto_walk(&mut self.auto_walk, &mut self.key_state, &mut self.hud); } self.key_state.right = state }, @@ -509,12 +515,12 @@ impl PlayState for SessionState { Event::InputUpdate(GameInput::FreeLook, state) => { match (global_state.settings.gameplay.free_look_behavior, state) { (PressBehavior::Toggle, true) => { - free_look = !free_look; - self.hud.free_look(free_look); + self.free_look = !self.free_look; + self.hud.free_look(self.free_look); }, (PressBehavior::Hold, state) => { - free_look = state; - self.hud.free_look(free_look); + self.free_look = state; + self.hud.free_look(self.free_look); }, _ => {}, }; @@ -522,14 +528,14 @@ impl PlayState for SessionState { Event::InputUpdate(GameInput::AutoWalk, state) => { match (global_state.settings.gameplay.auto_walk_behavior, state) { (PressBehavior::Toggle, true) => { - auto_walk = !auto_walk; - self.key_state.auto_walk = auto_walk; - self.hud.auto_walk(auto_walk); + self.auto_walk = !self.auto_walk; + self.key_state.auto_walk = self.auto_walk; + self.hud.auto_walk(self.auto_walk); }, (PressBehavior::Hold, state) => { - auto_walk = state; - self.key_state.auto_walk = auto_walk; - self.hud.auto_walk(auto_walk); + self.auto_walk = state; + self.key_state.auto_walk = self.auto_walk; + self.hud.auto_walk(self.auto_walk); }, _ => {}, } @@ -567,9 +573,9 @@ impl PlayState for SessionState { } } - if !free_look { - walk_forward_dir = self.scene.camera().forward_xy(); - walk_right_dir = self.scene.camera().right_xy(); + if !self.free_look { + self.walk_forward_dir = self.scene.camera().forward_xy(); + self.walk_right_dir = self.scene.camera().right_xy(); self.inputs.look_dir = Dir::from_unnormalized(cam_dir + aim_dir_offset).unwrap(); } @@ -581,8 +587,9 @@ impl PlayState for SessionState { camera::CameraMode::FirstPerson | camera::CameraMode::ThirdPerson => { // Move the player character based on their walking direction. // This could be different from the camera direction if free look is enabled. - self.inputs.move_dir = walk_right_dir * axis_right + walk_forward_dir * axis_up; - freefly_vel = Vec3::zero(); + self.inputs.move_dir = + self.walk_right_dir * axis_right + self.walk_forward_dir * axis_up; + self.freefly_vel = Vec3::zero(); }, camera::CameraMode::Freefly => { @@ -596,27 +603,27 @@ impl PlayState for SessionState { let right = self.scene.camera().right(); let dir = right * axis_right + forward * axis_up; - let dt = clock.get_last_delta().as_secs_f32(); - if freefly_vel.magnitude_squared() > 0.01 { - let new_vel = - freefly_vel - freefly_vel.normalized() * (FREEFLY_DAMPING * dt); - if freefly_vel.dot(new_vel) > 0.0 { - freefly_vel = new_vel; + let dt = global_state.clock.get_last_delta().as_secs_f32(); + if self.freefly_vel.magnitude_squared() > 0.01 { + let new_vel = self.freefly_vel + - self.freefly_vel.normalized() * (FREEFLY_DAMPING * dt); + if self.freefly_vel.dot(new_vel) > 0.0 { + self.freefly_vel = new_vel; } else { - freefly_vel = Vec3::zero(); + self.freefly_vel = Vec3::zero(); } } if dir.magnitude_squared() > 0.01 { - freefly_vel += dir * (FREEFLY_ACCEL * dt); - if freefly_vel.magnitude() > FREEFLY_MAX_SPEED { - freefly_vel = freefly_vel.normalized() * FREEFLY_MAX_SPEED; + self.freefly_vel += dir * (FREEFLY_ACCEL * dt); + if self.freefly_vel.magnitude() > FREEFLY_MAX_SPEED { + self.freefly_vel = self.freefly_vel.normalized() * FREEFLY_MAX_SPEED; } } let pos = self.scene.camera().get_focus_pos(); self.scene .camera_mut() - .set_focus_pos(pos + freefly_vel * dt); + .set_focus_pos(pos + self.freefly_vel * dt); // Do not apply any movement to the player character self.inputs.move_dir = Vec2::zero(); @@ -630,7 +637,7 @@ impl PlayState for SessionState { || !global_state.singleplayer.as_ref().unwrap().is_paused() { // Perform an in-game tick. - match self.tick(clock.get_avg_delta(), global_state) { + match self.tick(global_state.clock.get_avg_delta(), global_state) { Ok(TickAction::Continue) => {}, // Do nothing Ok(TickAction::Disconnect) => return PlayStateResult::Pop, // Go to main menu Err(err) => { @@ -643,19 +650,17 @@ impl PlayState for SessionState { } } - // Maintain global state. - global_state.maintain(clock.get_last_delta().as_secs_f32()); - // Recompute dependents just in case some input modified the camera self.scene .camera_mut() .compute_dependents(&*self.client.borrow().state().terrain()); + // Extract HUD events ensuring the client borrow gets dropped. let mut hud_events = self.hud.maintain( &self.client.borrow(), global_state, DebugInfo { - tps: clock.get_tps(), + tps: global_state.clock.get_tps(), ping_ms: self.client.borrow().get_ping_ms_rolling_avg(), coordinates: self .client @@ -687,7 +692,7 @@ impl PlayState for SessionState { num_figures_visible: self.scene.figure_mgr().figure_count_visible() as u32, }, &self.scene.camera(), - clock.get_last_delta(), + global_state.clock.get_last_delta(), HudInfo { is_aiming, is_first_person: matches!( @@ -697,8 +702,9 @@ impl PlayState for SessionState { }, ); + // TODO: dont // Look for changes in the localization files - if localization_watcher.reloaded() { + if global_state.localization_watcher.reloaded() { hud_events.push(HudEvent::ChangeLanguage(localized_strings.metadata.clone())); } @@ -916,9 +922,9 @@ impl PlayState for SessionState { HudEvent::ChangeLanguage(new_language) => { global_state.settings.language.selected_language = new_language.language_identifier; - localized_strings = load_watched::( + let localized_strings = load_watched::( &i18n_asset_key(&global_state.settings.language.selected_language), - &mut localization_watcher, + &mut global_state.localization_watcher, ) .unwrap(); localized_strings.log_missing_entries(); @@ -1005,32 +1011,53 @@ impl PlayState for SessionState { renderer.flush(); } - // Display the frame on the window. - global_state - .window - .swap_buffers() - .expect("Failed to swap window buffers!"); - - // Wait for the next tick. - clock.tick(Duration::from_millis( - 1000 / global_state.settings.graphics.max_fps as u64, - )); - // Clean things up after the tick. self.cleanup(); - current_client_state = self.client.borrow().get_client_state(); - } - - if let ClientState::Registered = current_client_state { - return PlayStateResult::Switch(Box::new(CharSelectionState::new( + PlayStateResult::Continue + } else if let ClientState::Registered = client_state { + PlayStateResult::Switch(Box::new(CharSelectionState::new( global_state, self.client.clone(), - ))); + ))) + } else { + error!("Client not in the expected state, exiting session play state"); + PlayStateResult::Pop } - - PlayStateResult::Pop } fn name(&self) -> &'static str { "Session" } + + /// Render the session to the screen. + /// + /// This method should be called once per frame. + fn render(&mut self, renderer: &mut Renderer, settings: &Settings) { + // Render the screen using the global renderer + { + let client = self.client.borrow(); + + let scene_data = SceneData { + state: client.state(), + player_entity: client.entity(), + loaded_distance: client.loaded_distance(), + view_distance: client.view_distance().unwrap_or(1), + tick: client.get_tick(), + thread_pool: client.thread_pool(), + gamma: settings.graphics.gamma, + mouse_smoothing: settings.gameplay.smooth_pan_enable, + sprite_render_distance: settings.graphics.sprite_render_distance as f32, + figure_lod_render_distance: settings.graphics.figure_lod_render_distance as f32, + is_aiming: self.is_aiming, + }; + self.scene.render( + renderer, + client.state(), + client.entity(), + client.get_tick(), + &scene_data, + ); + } + // Draw the UI to the screen + self.hud.render(renderer, self.scene.globals()); + } } diff --git a/voxygen/src/ui/event.rs b/voxygen/src/ui/event.rs index e7c3b6f2bb..3b4c74cf9e 100644 --- a/voxygen/src/ui/event.rs +++ b/voxygen/src/ui/event.rs @@ -20,7 +20,7 @@ impl Event { Some(winit::window::Window::inner_size(&self.0).into()) } - fn hidpi_factor(&self) -> f32 { winit::window::Window::get_hidpi_factor(&self.0) as _ } + fn hidpi_factor(&self) -> f32 { winit::window::Window::hidpi_factor(&self.0) as _ } } convert_event!(event, &WindowRef(window.window())).map(|input| Self(input)) } diff --git a/voxygen/src/window.rs b/voxygen/src/window.rs index 67bcf6f03c..3d3ff8cce0 100644 --- a/voxygen/src/window.rs +++ b/voxygen/src/window.rs @@ -282,17 +282,18 @@ pub enum Event { pub type MouseButton = winit::event::MouseButton; pub type PressState = winit::event::ElementState; +pub type EventLoop = winit::event_loop::EventLoop<()>; #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)] pub enum KeyMouse { - Key(glutin::event::VirtualKeyCode), - Mouse(glutin::event::MouseButton), + Key(winit::event::VirtualKeyCode), + Mouse(winit::event::MouseButton), } impl fmt::Display for KeyMouse { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { use self::KeyMouse::*; - use glutin::event::{MouseButton, VirtualKeyCode::*}; + use winit::event::{MouseButton, VirtualKeyCode::*}; write!(f, "{}", match self { Key(Key1) => "1", Key(Key2) => "2", @@ -466,7 +467,6 @@ impl fmt::Display for KeyMouse { } pub struct Window { - events_loop: glutin::event_loop::EventLoop<()>, renderer: Renderer, window: glutin::ContextWrapper, cursor_grabbed: bool, @@ -479,6 +479,7 @@ pub struct Window { keypress_map: HashMap, pub remapping_keybindings: Option, supplement_events: Vec, + events: Vec, focused: bool, gilrs: Option, controller_settings: ControllerSettings, @@ -490,14 +491,14 @@ pub struct Window { } impl Window { - pub fn new(settings: &Settings) -> Result { - let events_loop = glutin::event_loop::EventLoop::new(); + pub fn new(settings: &Settings) -> Result<(Window, EventLoop), Error> { + let event_loop = EventLoop::new(); let size = settings.graphics.window_size; let win_builder = glutin::window::WindowBuilder::new() .with_title("Veloren") - .with_dimensions(glutin::dpi::LogicalSize::new( + .with_inner_size(glutin::dpi::LogicalSize::new( size[0] as f64, size[1] as f64, )) @@ -508,10 +509,10 @@ impl Window { .with_vsync(false); let (window, device, factory, win_color_view, win_depth_view) = - gfx_window_glutin::init::( + gfx_window_glutin::init::( win_builder, ctx_builder, - &events_loop, + &event_loop, ) .map_err(|err| Error::BackendError(Box::new(err)))?; @@ -559,7 +560,6 @@ impl Window { ) = channel::unbounded::(); let mut this = Self { - events_loop, renderer: Renderer::new( device, factory, @@ -579,7 +579,8 @@ impl Window { needs_refresh_resize: false, keypress_map, remapping_keybindings: None, - supplement_events: vec![], + supplement_events: Vec::new(), + events: Vec::new(), focused: true, gilrs, controller_settings, @@ -592,7 +593,13 @@ impl Window { this.fullscreen(settings.graphics.fullscreen); - Ok(this) + Ok((this, event_loop)) + } + + pub fn window( + &self, + ) -> &glutin::ContextWrapper { + &self.window } pub fn renderer(&self) -> &Renderer { &self.renderer } @@ -600,12 +607,15 @@ impl Window { pub fn renderer_mut(&mut self) -> &mut Renderer { &mut self.renderer } #[allow(clippy::match_bool)] // TODO: Pending review in #587 - pub fn fetch_events(&mut self, settings: &mut Settings) -> Vec { - let mut events = vec![]; + pub fn fetch_events(&mut self) -> Vec { + let mut events = std::mem::take(&mut self.events); + events.append(&mut self.supplement_events); + // Refresh ui size (used when changing playstates) if self.needs_refresh_resize { - events.push(Event::Ui(ui::Event::new_resize(self.logical_size()))); + self.events + .push(Event::Ui(ui::Event::new_resize(self.logical_size()))); self.needs_refresh_resize = false; } @@ -614,181 +624,6 @@ impl Window { .try_iter() .for_each(|message| events.push(Event::ScreenshotMessage(message))); - // Copy data that is needed by the events closure to avoid lifetime errors. - // TODO: Remove this if/when the compiler permits it. - let cursor_grabbed = self.cursor_grabbed; - let renderer = &mut self.renderer; - let window = &mut self.window; - let remapping_keybindings = &mut self.remapping_keybindings; - let focused = &mut self.focused; - let controls = &mut settings.controls; - let keypress_map = &mut self.keypress_map; - let pan_sensitivity = self.pan_sensitivity; - let zoom_sensitivity = self.zoom_sensitivity; - let zoom_inversion = match self.zoom_inversion { - true => -1.0, - false => 1.0, - }; - let mouse_y_inversion = match self.mouse_y_inversion { - true => -1.0, - false => 1.0, - }; - let mut toggle_fullscreen = false; - let mut take_screenshot = false; - let mut cursor_position = None; - - self.events_loop.poll_events(|event| { - // Get events for ui. - if let Some(event) = ui::Event::try_from(event.clone(), window) { - events.push(Event::Ui(event)); - } - - match event { - glutin::event::Event::WindowEvent { event, .. } => match event { - glutin::event::WindowEvent::CloseRequested => events.push(Event::Close), - glutin::event::WindowEvent::Resized(glutin::dpi::LogicalSize { - width, - height, - }) => { - let (mut color_view, mut depth_view) = renderer.win_views_mut(); - gfx_window_glutin::update_views(window, &mut color_view, &mut depth_view); - renderer.on_resize().unwrap(); - events.push(Event::Resize(Vec2::new(width as u32, height as u32))); - }, - glutin::event::WindowEvent::Moved(glutin::dpi::LogicalPosition { x, y }) => { - events.push(Event::Moved(Vec2::new(x as u32, y as u32))) - }, - glutin::event::WindowEvent::ReceivedCharacter(c) => events.push(Event::Char(c)), - glutin::event::WindowEvent::MouseInput { button, state, .. } => { - if let (true, Some(game_inputs)) = ( - cursor_grabbed, - Window::map_input( - KeyMouse::Mouse(button), - controls, - remapping_keybindings, - ), - ) { - for game_input in game_inputs { - events.push(Event::InputUpdate( - *game_input, - state == glutin::event::ElementState::Pressed, - )); - } - } - events.push(Event::MouseButton(button, state)); - }, - glutin::event::WindowEvent::KeyboardInput { input, .. } => { - if let Some(key) = input.virtual_keycode { - if let Some(game_inputs) = Window::map_input( - KeyMouse::Key(key), - controls, - remapping_keybindings, - ) { - for game_input in game_inputs { - match game_input { - GameInput::Fullscreen => { - if input.state == glutin::event::ElementState::Pressed - && !Self::is_pressed( - keypress_map, - GameInput::Fullscreen, - ) - { - toggle_fullscreen = !toggle_fullscreen; - } - Self::set_pressed( - keypress_map, - GameInput::Fullscreen, - input.state, - ); - }, - GameInput::Screenshot => { - take_screenshot = input.state - == glutin::event::ElementState::Pressed - && !Self::is_pressed( - keypress_map, - GameInput::Screenshot, - ); - Self::set_pressed( - keypress_map, - GameInput::Screenshot, - input.state, - ); - }, - _ => events.push(Event::InputUpdate( - *game_input, - input.state == glutin::event::ElementState::Pressed, - )), - } - } - } - } - }, - glutin::event::WindowEvent::Focused(state) => { - *focused = state; - events.push(Event::Focused(state)); - }, - glutin::event::WindowEvent::CursorMoved { position, .. } => { - cursor_position = Some(position); - }, - _ => {}, - }, - glutin::event::Event::DeviceEvent { event, .. } => match event { - glutin::event::DeviceEvent::MouseMotion { - delta: (dx, dy), .. - } if *focused => { - let delta = Vec2::new( - dx as f32 * (pan_sensitivity as f32 / 100.0), - dy as f32 * (pan_sensitivity as f32 * mouse_y_inversion / 100.0), - ); - - if cursor_grabbed { - events.push(Event::CursorPan(delta)); - } else { - events.push(Event::CursorMove(delta)); - } - }, - glutin::event::DeviceEvent::MouseWheel { delta, .. } - if cursor_grabbed && *focused => - { - events.push(Event::Zoom({ - // Since scrolling apparently acts different depending on platform - #[cfg(target_os = "windows")] - const PLATFORM_FACTOR: f32 = -4.0; - #[cfg(not(target_os = "windows"))] - const PLATFORM_FACTOR: f32 = 1.0; - - let y = match delta { - glutin::event::MouseScrollDelta::LineDelta(_x, y) => y, - // TODO: Check to see if there is a better way to find the "line - // height" than just hardcoding 16.0 pixels. Alternately we could - // get rid of this and have the user set zoom sensitivity, since - // it's unlikely people would expect a configuration file to work - // across operating systems. - glutin::event::MouseScrollDelta::PixelDelta(pos) => { - (pos.y / 16.0) as f32 - }, - }; - y * (zoom_sensitivity as f32 / 100.0) * zoom_inversion * PLATFORM_FACTOR - })) - } - _ => {}, - }, - _ => {}, - } - }); - - if let Some(pos) = cursor_position { - self.cursor_position = pos; - } - - if take_screenshot { - self.take_screenshot(&settings); - } - - if toggle_fullscreen { - self.toggle_fullscreen(settings); - } - if let Some(gilrs) = &mut self.gilrs { while let Some(event) = gilrs.next_event() { fn handle_buttons( @@ -814,7 +649,7 @@ impl Window { | EventType::ButtonRepeated(button, code) => { handle_buttons( &self.controller_settings, - &mut events, + &mut self.events, &Button::from((button, code)), true, ); @@ -822,7 +657,7 @@ impl Window { EventType::ButtonReleased(button, code) => { handle_buttons( &self.controller_settings, - &mut events, + &mut self.events, &Button::from((button, code)), false, ); @@ -871,17 +706,17 @@ impl Window { for action in actions { match *action { AxisGameAction::MovementX => { - events.push(Event::AnalogGameInput( + self.events.push(Event::AnalogGameInput( AnalogGameInput::MovementX(value), )); }, AxisGameAction::MovementY => { - events.push(Event::AnalogGameInput( + self.events.push(Event::AnalogGameInput( AnalogGameInput::MovementY(value), )); }, AxisGameAction::CameraX => { - events.push(Event::AnalogGameInput( + self.events.push(Event::AnalogGameInput( AnalogGameInput::CameraX( value * self.controller_settings.pan_sensitivity @@ -891,7 +726,7 @@ impl Window { )); }, AxisGameAction::CameraY => { - events.push(Event::AnalogGameInput( + self.events.push(Event::AnalogGameInput( AnalogGameInput::CameraY( value * self.controller_settings.pan_sensitivity @@ -912,22 +747,22 @@ impl Window { for action in actions { match *action { AxisMenuAction::MoveX => { - events.push(Event::AnalogMenuInput( + self.events.push(Event::AnalogMenuInput( AnalogMenuInput::MoveX(value), )); }, AxisMenuAction::MoveY => { - events.push(Event::AnalogMenuInput( + self.events.push(Event::AnalogMenuInput( AnalogMenuInput::MoveY(value), )); }, AxisMenuAction::ScrollX => { - events.push(Event::AnalogMenuInput( + self.events.push(Event::AnalogMenuInput( AnalogMenuInput::ScrollX(value), )); }, AxisMenuAction::ScrollY => { - events.push(Event::AnalogMenuInput( + self.events.push(Event::AnalogMenuInput( AnalogMenuInput::ScrollY(value), )); }, @@ -945,7 +780,8 @@ impl Window { // Mouse emulation for the menus, to be removed when a proper menu navigation // system is available if !self.cursor_grabbed { - events = events + self.events = self + .events .into_iter() .filter_map(|event| match event { Event::AnalogMenuInput(input) => match input { @@ -981,23 +817,186 @@ impl Window { _ => Some(event), }) .collect(); + let sensitivity = self.controller_settings.mouse_emulation_sensitivity; - if self.mouse_emulation_vec != Vec2::zero() { - self.offset_cursor(self.mouse_emulation_vec * sensitivity as f32) - .unwrap_or(()); - } + // TODO: make this independent of framerate + self.offset_cursor(self.mouse_emulation_vec * sensitivity as f32); + } + + std::mem::replace(&mut self.events, Vec::new()) + } + + pub fn handle_device_event(&mut self, event: winit::event::DeviceEvent) { + use winit::event::DeviceEvent; + + let mouse_y_inversion = match self.mouse_y_inversion { + true => -1.0, + false => 1.0, + }; + + match event { + DeviceEvent::MouseMotion { + delta: (dx, dy), .. + } if self.focused => { + let delta = Vec2::new( + dx as f32 * (self.pan_sensitivity as f32 / 100.0), + dy as f32 * (self.pan_sensitivity as f32 * mouse_y_inversion / 100.0), + ); + + if self.cursor_grabbed { + self.events.push(Event::CursorPan(delta)); + } else { + self.events.push(Event::CursorMove(delta)); + } + }, + DeviceEvent::MouseWheel { delta, .. } if self.cursor_grabbed && self.focused => { + self.events.push(Event::Zoom({ + // Since scrolling apparently acts different depending on platform + #[cfg(target_os = "windows")] + const PLATFORM_FACTOR: f32 = -4.0; + #[cfg(not(target_os = "windows"))] + const PLATFORM_FACTOR: f32 = 1.0; + + let y = match delta { + winit::event::MouseScrollDelta::LineDelta(_x, y) => y, + // TODO: Check to see if there is a better way to find the "line + // height" than just hardcoding 16.0 pixels. Alternately we could + // get rid of this and have the user set zoom sensitivity, since + // it's unlikely people would expect a configuration file to work + // across operating systems. + winit::event::MouseScrollDelta::PixelDelta(pos) => (pos.y / 16.0) as f32, + }; + y * (self.zoom_sensitivity as f32 / 100.0) + * if self.zoom_inversion { -1.0 } else { 1.0 } + * PLATFORM_FACTOR + })) + }, + _ => {}, + } + } + + pub fn handle_window_event( + &mut self, + event: winit::event::WindowEvent, + settings: &mut Settings, + ) { + use winit::event::WindowEvent; + + let controls = &mut settings.controls; + // TODO: these used to be used to deduplicate events which they no longer do + // this needs to be handled elsewhere + let mut toggle_fullscreen = false; + let mut take_screenshot = false; + + match event { + WindowEvent::CloseRequested => self.events.push(Event::Close), + WindowEvent::Resized(winit::dpi::LogicalSize { width, height }) => { + let (mut color_view, mut depth_view) = self.renderer.win_views_mut(); + gfx_window_glutin::update_views(&self.window, &mut color_view, &mut depth_view); + self.renderer.on_resize().unwrap(); + self.events + .push(Event::Resize(Vec2::new(width as u32, height as u32))); + }, + WindowEvent::ReceivedCharacter(c) => self.events.push(Event::Char(c)), + WindowEvent::MouseInput { button, state, .. } => { + if let (true, Some(game_inputs)) = + // Mouse input not mapped to input if it is not grabbed + ( + self.cursor_grabbed, + Window::map_input( + KeyMouse::Mouse(button), + controls, + &mut self.remapping_keybindings, + ), + ) { + for game_input in game_inputs { + self.events.push(Event::InputUpdate( + *game_input, + state == winit::event::ElementState::Pressed, + )); + } + } + self.events.push(Event::MouseButton(button, state)); + }, + WindowEvent::KeyboardInput { input, .. } => { + if let Some(key) = input.virtual_keycode { + if let Some(game_inputs) = Window::map_input( + KeyMouse::Key(key), + controls, + &mut self.remapping_keybindings, + ) { + for game_input in game_inputs { + match game_input { + GameInput::Fullscreen => { + if input.state == winit::event::ElementState::Pressed + && !Self::is_pressed( + &mut self.keypress_map, + GameInput::Fullscreen, + ) + { + toggle_fullscreen = !toggle_fullscreen; + } + Self::set_pressed( + &mut self.keypress_map, + GameInput::Fullscreen, + input.state, + ); + }, + GameInput::Screenshot => { + take_screenshot = input.state + == winit::event::ElementState::Pressed + && !Self::is_pressed( + &mut self.keypress_map, + GameInput::Screenshot, + ); + Self::set_pressed( + &mut self.keypress_map, + GameInput::Screenshot, + input.state, + ); + }, + _ => self.events.push(Event::InputUpdate( + *game_input, + input.state == winit::event::ElementState::Pressed, + )), + } + } + } + } + }, + WindowEvent::Focused(state) => { + self.focused = state; + self.events.push(Event::Focused(state)); + }, + WindowEvent::CursorMoved { position, .. } => { + self.cursor_position = position; + }, + _ => {}, + } + + if take_screenshot { + self.take_screenshot(&settings); + } + + if toggle_fullscreen { + self.toggle_fullscreen(settings); } - events } /// Moves cursor by an offset - pub fn offset_cursor(&self, d: Vec2) -> Result<(), String> { - self.window - .window() - .set_cursor_position(winit::dpi::LogicalPosition::new( - d.x as f64 + self.cursor_position.x, - d.y as f64 + self.cursor_position.y, - )) + pub fn offset_cursor(&self, d: Vec2) { + if d != Vec2::zero() { + if let Err(err) = + self.window + .window() + .set_cursor_position(winit::dpi::LogicalPosition::new( + d.x as f64 + self.cursor_position.x, + d.y as f64 + self.cursor_position.y, + )) + { + error!("Error setting cursor position: {:?}", err); + } + } } pub fn swap_buffers(&self) -> Result<(), Error> { @@ -1051,6 +1050,8 @@ impl Window { )); } + pub fn send_event(&mut self, event: Event) { self.events.push(event) } + pub fn send_supplement_event(&mut self, event: Event) { self.supplement_events.push(event) } pub fn take_screenshot(&mut self, settings: &Settings) { @@ -1090,19 +1091,19 @@ impl Window { } fn is_pressed( - map: &mut HashMap, + map: &mut HashMap, input: GameInput, ) -> bool { *(map .entry(input) - .or_insert(glutin::event::ElementState::Released)) - == glutin::event::ElementState::Pressed + .or_insert(winit::event::ElementState::Released)) + == winit::event::ElementState::Pressed } fn set_pressed( - map: &mut HashMap, + map: &mut HashMap, input: GameInput, - state: glutin::event::ElementState, + state: winit::event::ElementState, ) { map.insert(input, state); } @@ -1117,6 +1118,7 @@ impl Window { remapping: &mut Option, ) -> Option> { match *remapping { + // TODO: save settings Some(game_input) => { controls.modify_binding(game_input, key_mouse); *remapping = None; From 21a138285010652826d32db0526b85e6ece8375d Mon Sep 17 00:00:00 2001 From: Imbris Date: Fri, 7 Feb 2020 21:09:56 -0500 Subject: [PATCH 46/85] Update to winit 0.21.0 --- Cargo.lock | 269 ++++++++++++++++++++++++----------- voxygen/Cargo.toml | 9 +- voxygen/src/menu/main/mod.rs | 2 - voxygen/src/run.rs | 15 +- voxygen/src/ui/event.rs | 10 +- voxygen/src/window.rs | 70 +++++---- 6 files changed, 239 insertions(+), 136 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1c866300d8..173829e19c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -482,6 +482,15 @@ dependencies = [ "libc", ] +[[package]] +name = "cgl" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ced0551234e87afee12411d535648dd89d2e7f34c78b753395567aff3d447ff" +dependencies = [ + "libc", +] + [[package]] name = "chrono" version = "0.4.11" @@ -674,7 +683,7 @@ dependencies = [ "objc-foundation", "objc_id", "smithay-clipboard", - "wayland-client", + "wayland-client 0.23.6", "x11-clipboard", ] @@ -1044,17 +1053,6 @@ dependencies = [ "byteorder 1.3.4", ] -[[package]] -name = "derivative" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c6d883546668a3e2011b6a716a7330b82eabb0151b138217f632c8243e17135" -dependencies = [ - "proc-macro2 0.4.30", - "quote 0.6.13", - "syn 0.15.44", -] - [[package]] name = "deunicode" version = "1.1.1" @@ -1126,6 +1124,12 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04e93ca78226c51902d7aa8c12c988338aadd9e85ed9c6be8aaac39192ff3605" +[[package]] +name = "dispatch" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" + [[package]] name = "dlib" version = "0.4.2" @@ -1571,11 +1575,12 @@ dependencies = [ [[package]] name = "gfx_window_glutin" version = "0.31.0" -source = "git+https://github.com/Imberflur/gfx_window_glutin.git#6586513e6b22924e61d1106e426768ac4f1cb55d" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "310ff66f08b5a55854b18fea2f48bdbb75c94458207ba574c9723be78e97a646" dependencies = [ "gfx_core", "gfx_device_gl", - "glutin", + "glutin 0.21.2", ] [[package]] @@ -1734,16 +1739,41 @@ dependencies = [ [[package]] name = "glutin" -version = "0.22.0-alpha3" +version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcdd3592b515014281a21a42addd91f41eaff0c5f54295ee28dac8ea6bbbceba" +checksum = "5371b35b309dace06be1b81b5f6adb1c9de578b7dbe1e74bf7e4ef762cf6febd" dependencies = [ "android_glue", - "cgl", + "cgl 0.2.3", "cocoa 0.18.5", "core-foundation 0.6.4", "core-graphics 0.17.3", - "derivative", + "glutin_egl_sys", + "glutin_emscripten_sys", + "glutin_gles2_sys", + "glutin_glx_sys", + "glutin_wgl_sys", + "lazy_static", + "libloading 0.5.2", + "objc", + "osmesa-sys", + "parking_lot 0.9.0", + "wayland-client 0.21.13", + "winapi 0.3.8", + "winit 0.19.5", +] + +[[package]] +name = "glutin" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf22d4e90c55d9be9f2ad52410e7a2c0d7e9c99d93a13df73a672e7ef4e8c7f7" +dependencies = [ + "android_glue", + "cgl 0.3.2", + "cocoa 0.19.1", + "core-foundation 0.6.4", + "core-graphics 0.17.3", "glutin_egl_sys", "glutin_emscripten_sys", "glutin_gles2_sys", @@ -1754,10 +1784,10 @@ dependencies = [ "log", "objc", "osmesa-sys", - "parking_lot 0.8.0", - "wayland-client", + "parking_lot 0.10.2", + "wayland-client 0.23.6", "winapi 0.3.8", - "winit", + "winit 0.21.0", ] [[package]] @@ -2307,15 +2337,6 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8dd5a6d5999d9907cda8ed67bbd137d3af8085216c2ac62de5be860bd41f304a" -[[package]] -name = "lock_api" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed946d4529956a20f2d63ebe1b69996d5a2137c91913fe3ebbeff957f5bca7ff" -dependencies = [ - "scopeguard", -] - [[package]] name = "lock_api" version = "0.3.4" @@ -2755,6 +2776,16 @@ dependencies = [ "byteorder 1.3.4", ] +[[package]] +name = "old_school_gfx_glutin_ext" +version = "0.23.0" +source = "git+https://github.com/Imberflur/old-school-gfx-glutin-ext.git#e128e187a81d1fa15409db38bc93e6677fc09288" +dependencies = [ + "gfx_core", + "gfx_device_gl", + "glutin 0.23.0", +] + [[package]] name = "once_cell" version = "1.4.0" @@ -2850,24 +2881,13 @@ dependencies = [ "pkg-config", ] -[[package]] -name = "parking_lot" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa7767817701cce701d5585b9c4db3cdd02086398322c1d7e8bf5094a96a2ce7" -dependencies = [ - "lock_api 0.2.0", - "parking_lot_core 0.5.0", - "rustc_version", -] - [[package]] name = "parking_lot" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252" dependencies = [ - "lock_api 0.3.4", + "lock_api", "parking_lot_core 0.6.2", "rustc_version", ] @@ -2878,26 +2898,10 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3a704eb390aafdc107b0e392f56a82b668e3a71366993b5340f5833fd62505e" dependencies = [ - "lock_api 0.3.4", + "lock_api", "parking_lot_core 0.7.2", ] -[[package]] -name = "parking_lot_core" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb88cb1cb3790baa6776844f968fea3be44956cf184fa1be5a03341f5491278c" -dependencies = [ - "cfg-if", - "cloudabi", - "libc", - "rand 0.6.5", - "redox_syscall", - "rustc_version", - "smallvec 0.6.13", - "winapi 0.3.8", -] - [[package]] name = "parking_lot_core" version = "0.6.2" @@ -3771,6 +3775,23 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7cb5678e1615754284ec264d9bb5b4c27d2018577fd90ac0ceb578591ed5ee4" +[[package]] +name = "smithay-client-toolkit" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ccb8c57049b2a34d2cc2b203fa785020ba0129d31920ef0d317430adaf748fa" +dependencies = [ + "andrew", + "bitflags", + "dlib", + "lazy_static", + "memmap", + "nix 0.14.1", + "wayland-client 0.21.13", + "wayland-commons 0.21.13", + "wayland-protocols 0.21.13", +] + [[package]] name = "smithay-client-toolkit" version = "0.6.6" @@ -3783,8 +3804,8 @@ dependencies = [ "lazy_static", "memmap", "nix 0.14.1", - "wayland-client", - "wayland-protocols", + "wayland-client 0.23.6", + "wayland-protocols 0.23.6", ] [[package]] @@ -3794,7 +3815,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "917e8ec7f535cd1a6cbf749c8866c24d67c548a80ac48c8e88a182eab5c07bd1" dependencies = [ "nix 0.14.1", - "smithay-client-toolkit", + "smithay-client-toolkit 0.6.6", ] [[package]] @@ -4589,7 +4610,7 @@ dependencies = [ "crossbeam", "deunicode", "directories-next", - "dispatch", + "dispatch 0.1.4", "dot_vox", "euc", "failure", @@ -4599,12 +4620,13 @@ dependencies = [ "gilrs", "git2", "glsl-include", - "glutin", + "glutin 0.23.0", "guillotiere", "hashbrown", "image", "msgbox", "num 0.2.1", + "old_school_gfx_glutin_ext", "rand 0.7.3", "rodio", "ron", @@ -4624,7 +4646,7 @@ dependencies = [ "veloren-server", "veloren-voxygen-anim", "veloren-world", - "winit", + "winit 0.21.0", "winres", ] @@ -4789,6 +4811,21 @@ version = "0.2.63" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c9ba19973a58daf4db6f352eda73dc0e289493cd29fb2632eb172085b6521acd" +[[package]] +name = "wayland-client" +version = "0.21.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49963e5f9eeaf637bfcd1b9f0701c99fd5cd05225eb51035550d4272806f2713" +dependencies = [ + "bitflags", + "downcast-rs", + "libc", + "nix 0.14.1", + "wayland-commons 0.21.13", + "wayland-scanner 0.21.13", + "wayland-sys 0.21.13", +] + [[package]] name = "wayland-client" version = "0.23.6" @@ -4801,9 +4838,19 @@ dependencies = [ "libc", "mio", "nix 0.14.1", - "wayland-commons", - "wayland-scanner", - "wayland-sys", + "wayland-commons 0.23.6", + "wayland-scanner 0.23.6", + "wayland-sys 0.23.6", +] + +[[package]] +name = "wayland-commons" +version = "0.21.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40c08896768b667e1df195d88a62a53a2d1351a1ed96188be79c196b35bb32ec" +dependencies = [ + "nix 0.14.1", + "wayland-sys 0.21.13", ] [[package]] @@ -4813,7 +4860,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb66b0d1a27c39bbce712b6372131c6e25149f03ffb0cd017cf8f7de8d66dbdb" dependencies = [ "nix 0.14.1", - "wayland-sys", + "wayland-sys 0.23.6", +] + +[[package]] +name = "wayland-protocols" +version = "0.21.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4afde2ea2a428eee6d7d2c8584fdbe8b82eee8b6c353e129a434cd6e07f42145" +dependencies = [ + "bitflags", + "wayland-client 0.21.13", + "wayland-commons 0.21.13", + "wayland-scanner 0.21.13", + "wayland-sys 0.21.13", ] [[package]] @@ -4823,9 +4883,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6cc286643656742777d55dc8e70d144fa4699e426ca8e9d4ef454f4bf15ffcf9" dependencies = [ "bitflags", - "wayland-client", - "wayland-commons", - "wayland-scanner", + "wayland-client 0.23.6", + "wayland-commons 0.23.6", + "wayland-scanner 0.23.6", +] + +[[package]] +name = "wayland-scanner" +version = "0.21.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf3828c568714507315ee425a9529edc4a4aa9901409e373e9e0027e7622b79e" +dependencies = [ + "proc-macro2 0.4.30", + "quote 0.6.13", + "xml-rs", ] [[package]] @@ -4839,6 +4910,16 @@ dependencies = [ "xml-rs", ] +[[package]] +name = "wayland-sys" +version = "0.21.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520ab0fd578017a0ee2206623ba9ef4afe5e8f23ca7b42f6acfba2f4e66b1628" +dependencies = [ + "dlib", + "lazy_static", +] + [[package]] name = "wayland-sys" version = "0.23.6" @@ -4923,19 +5004,16 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "winit" -version = "0.20.0-alpha4" +version = "0.19.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56c565622ccb05351d92445415952ca09dade6a53e75dd9e75d9bd35d4e99333" +checksum = "1e96eb4bb472fa43e718e8fa4aef82f86cd9deac9483a1e1529230babdb394a8" dependencies = [ "android_glue", + "backtrace", "bitflags", - "calloop", - "cocoa 0.19.1", + "cocoa 0.18.5", "core-foundation 0.6.4", "core-graphics 0.17.3", - "core-video-sys", - "dispatch", - "instant", "lazy_static", "libc", "log", @@ -4943,9 +5021,38 @@ dependencies = [ "parking_lot 0.9.0", "percent-encoding 2.1.0", "raw-window-handle", + "smithay-client-toolkit 0.4.6", + "wayland-client 0.21.13", + "winapi 0.3.8", + "x11-dl", +] + +[[package]] +name = "winit" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65a5c1a5ef76ac31cc97ad29489acdbed2178f3fc12ca00ee6cb11d60adb5a3a" +dependencies = [ + "android_glue", + "bitflags", + "cocoa 0.19.1", + "core-foundation 0.6.4", + "core-graphics 0.17.3", + "core-video-sys", + "dispatch 0.2.0", + "instant", + "lazy_static", + "libc", + "log", + "mio", + "mio-extras", + "objc", + "parking_lot 0.10.2", + "percent-encoding 2.1.0", + "raw-window-handle", "serde", - "smithay-client-toolkit", - "wayland-client", + "smithay-client-toolkit 0.6.6", + "wayland-client 0.23.6", "winapi 0.3.8", "x11-dl", ] diff --git a/voxygen/Cargo.toml b/voxygen/Cargo.toml index 78d0f82045..6f976137c6 100644 --- a/voxygen/Cargo.toml +++ b/voxygen/Cargo.toml @@ -25,10 +25,9 @@ anim = { package = "veloren-voxygen-anim", path = "src/anim", default-features = # Graphics gfx = "0.18.2" gfx_device_gl = { version = "0.16.2", optional = true } -gfx_window_glutin = {git = "https://github.com/Imberflur/gfx_window_glutin.git" } -# TODO: change to non alpha -glutin = "=0.22.0-alpha3" -winit = { version = "=0.20.0-alpha4", features = ["serde"] } +old_school_gfx_glutin_ext = { git = "https://github.com/Imberflur/old-school-gfx-glutin-ext.git" } +glutin = "0.23.0" +winit = { version = "0.21.0", features = ["serde"] } conrod_core = { git = "https://gitlab.com/veloren/conrod.git", branch = "hide_text" } conrod_winit = { git = "https://gitlab.com/veloren/conrod.git", branch = "hide_text" } euc = { git = "https://github.com/zesterer/euc.git" } @@ -88,7 +87,7 @@ winres = "0.1" criterion = "0.3" git2 = "0.13" world = { package = "veloren-world", path = "../world" } -#gfx_window_glutin = { version = "0.31.0", features = ["headless"] } +gfx_window_glutin = { version = "0.31.0", features = ["headless"] } [[bench]] name = "meshing_benchmark" diff --git a/voxygen/src/menu/main/mod.rs b/voxygen/src/menu/main/mod.rs index e7465f5fec..5ea3ded5ab 100644 --- a/voxygen/src/menu/main/mod.rs +++ b/voxygen/src/menu/main/mod.rs @@ -1,7 +1,5 @@ mod client_init; mod ui; -// TODO: make sure turning off singleplayer feature still works (also make sure -// all singleplayer stuff is still hidden with it off) use super::char_selection::CharSelectionState; #[cfg(feature = "singleplayer")] diff --git a/voxygen/src/run.rs b/voxygen/src/run.rs index bc43253928..7105f15b2f 100644 --- a/voxygen/src/run.rs +++ b/voxygen/src/run.rs @@ -17,13 +17,16 @@ pub fn run(mut global_state: GlobalState, event_loop: EventLoop) { }); event_loop.run(move |event, _, control_flow| { + // Continously run loop since we handle sleeping + *control_flow = winit::event_loop::ControlFlow::Poll; + // Get events for the ui. - if let Some(event) = ui::Event::try_from(event.clone(), global_state.window.window()) { + if let Some(event) = ui::Event::try_from(&event, global_state.window.window()) { global_state.window.send_event(Event::Ui(event)); } match event { - winit::event::Event::EventsCleared => { + winit::event::Event::MainEventsCleared => { // Run tick here // What's going on here? @@ -94,11 +97,9 @@ pub fn run(mut global_state: GlobalState, event_loop: EventLoop) { } } - *control_flow = if exit { - winit::event_loop::ControlFlow::Exit - } else { - winit::event_loop::ControlFlow::Poll - }; + if exit { + *control_flow = winit::event_loop::ControlFlow::Exit; + } // TODO: move if let Some(last) = states.last_mut() { diff --git a/voxygen/src/ui/event.rs b/voxygen/src/ui/event.rs index 3b4c74cf9e..eed4160cb1 100644 --- a/voxygen/src/ui/event.rs +++ b/voxygen/src/ui/event.rs @@ -5,7 +5,7 @@ use vek::*; pub struct Event(pub Input); impl Event { pub fn try_from( - event: glutin::event::Event<()>, + event: &winit::event::Event<()>, window: &glutin::ContextWrapper, ) -> Option { use conrod_winit::*; @@ -17,10 +17,14 @@ impl Event { // compatible conversion functions. impl<'a> conrod_winit::WinitWindow for WindowRef<'a> { fn get_inner_size(&self) -> Option<(u32, u32)> { - Some(winit::window::Window::inner_size(&self.0).into()) + Some( + winit::window::Window::inner_size(&self.0) + .to_logical::(self.hidpi_factor()) + .into(), + ) } - fn hidpi_factor(&self) -> f32 { winit::window::Window::hidpi_factor(&self.0) as _ } + fn hidpi_factor(&self) -> f64 { winit::window::Window::scale_factor(&self.0) } } convert_event!(event, &WindowRef(window.window())).map(|input| Self(input)) } diff --git a/voxygen/src/window.rs b/voxygen/src/window.rs index 3d3ff8cce0..f229b8683c 100644 --- a/voxygen/src/window.rs +++ b/voxygen/src/window.rs @@ -4,10 +4,10 @@ use crate::{ settings::{ControlSettings, Settings}, ui, Error, }; +use crossbeam::channel; use gilrs::{EventType, Gilrs}; use hashbrown::HashMap; - -use crossbeam::channel; +use old_school_gfx_glutin_ext::{ContextBuilderExt, WindowInitExt, WindowUpdateExt}; use serde_derive::{Deserialize, Serialize}; use std::fmt; use tracing::{error, info, warn}; @@ -483,7 +483,7 @@ pub struct Window { focused: bool, gilrs: Option, controller_settings: ControllerSettings, - cursor_position: winit::dpi::LogicalPosition, + cursor_position: winit::dpi::PhysicalPosition, mouse_emulation_vec: Vec2, // Currently used to send and receive screenshot result messages message_sender: channel::Sender, @@ -504,17 +504,14 @@ impl Window { )) .with_maximized(true); - let ctx_builder = glutin::ContextBuilder::new() - .with_gl(glutin::GlRequest::Specific(glutin::Api::OpenGl, (3, 2))) - .with_vsync(false); - let (window, device, factory, win_color_view, win_depth_view) = - gfx_window_glutin::init::( - win_builder, - ctx_builder, - &event_loop, - ) - .map_err(|err| Error::BackendError(Box::new(err)))?; + glutin::ContextBuilder::new() + .with_gl(glutin::GlRequest::Specific(glutin::Api::OpenGl, (3, 2))) + .with_vsync(false) + .with_gfx_color_depth::() + .build_windowed(win_builder, &event_loop) + .map_err(|err| Error::BackendError(Box::new(err)))? + .init_gfx::(); let vendor = device.get_info().platform_name.vendor; let renderer = device.get_info().platform_name.renderer; @@ -584,7 +581,7 @@ impl Window { focused: true, gilrs, controller_settings, - cursor_position: winit::dpi::LogicalPosition::new(0.0, 0.0), + cursor_position: winit::dpi::PhysicalPosition::new(0.0, 0.0), mouse_emulation_vec: Vec2::zero(), // Currently used to send and receive screenshot result messages message_sender, @@ -777,11 +774,11 @@ impl Window { } } + let mut events = std::mem::replace(&mut self.events, Vec::new()); // Mouse emulation for the menus, to be removed when a proper menu navigation // system is available if !self.cursor_grabbed { - self.events = self - .events + events = events .into_iter() .filter_map(|event| match event { Event::AnalogMenuInput(input) => match input { @@ -794,36 +791,31 @@ impl Window { self.mouse_emulation_vec.y = d * -1.0; None }, - _ => { - let event = Event::AnalogMenuInput(input); - Some(event) - }, - }, - Event::MenuInput(input, state) => match input { - MenuInput::Apply => Some(match state { - true => Event::Ui(ui::Event(conrod_core::event::Input::Press( - conrod_core::input::Button::Mouse( - conrod_core::input::state::mouse::Button::Left, - ), - ))), - false => Event::Ui(ui::Event(conrod_core::event::Input::Release( - conrod_core::input::Button::Mouse( - conrod_core::input::state::mouse::Button::Left, - ), - ))), - }), - _ => Some(event), + input => Some(Event::AnalogMenuInput(input)), }, + Event::MenuInput(MenuInput::Apply, state) => Some(match state { + true => Event::Ui(ui::Event(conrod_core::event::Input::Press( + conrod_core::input::Button::Mouse( + conrod_core::input::state::mouse::Button::Left, + ), + ))), + false => Event::Ui(ui::Event(conrod_core::event::Input::Release( + conrod_core::input::Button::Mouse( + conrod_core::input::state::mouse::Button::Left, + ), + ))), + }), _ => Some(event), }) .collect(); let sensitivity = self.controller_settings.mouse_emulation_sensitivity; // TODO: make this independent of framerate + // TODO: consider multiplying by scale factor self.offset_cursor(self.mouse_emulation_vec * sensitivity as f32); } - std::mem::replace(&mut self.events, Vec::new()) + events } pub fn handle_device_event(&mut self, event: winit::event::DeviceEvent) { @@ -890,10 +882,12 @@ impl Window { match event { WindowEvent::CloseRequested => self.events.push(Event::Close), - WindowEvent::Resized(winit::dpi::LogicalSize { width, height }) => { + WindowEvent::Resized(winit::dpi::PhysicalSize { width, height }) => { let (mut color_view, mut depth_view) = self.renderer.win_views_mut(); - gfx_window_glutin::update_views(&self.window, &mut color_view, &mut depth_view); + self.window.update_gfx(&mut color_view, &mut depth_view); self.renderer.on_resize().unwrap(); + // TODO: update users of this event with the fact that it is now the physical + // size self.events .push(Event::Resize(Vec2::new(width as u32, height as u32))); }, From db91ebe9b5e759caf0e878f8898e141b4e407fba Mon Sep 17 00:00:00 2001 From: Imbris Date: Fri, 7 Feb 2020 21:39:01 -0500 Subject: [PATCH 47/85] fix a few minor bugs --- common/src/state.rs | 13 ++++--------- voxygen/src/session.rs | 31 +++++++++++++++++-------------- 2 files changed, 21 insertions(+), 23 deletions(-) diff --git a/common/src/state.rs b/common/src/state.rs index 64e8ef5511..f232d07163 100644 --- a/common/src/state.rs +++ b/common/src/state.rs @@ -249,16 +249,11 @@ impl State { /// Removes every chunk of the terrain. pub fn clear_terrain(&mut self) { - let keys = self - .terrain_mut() - .drain() - .map(|(key, _)| key) - .collect::>(); + let removed_chunks = &mut self.ecs.write_resource::().removed_chunks; - // TODO: fix that this does nothing - for key in keys { - self.remove_chunk(key); - } + self.terrain_mut().drain().for_each(|(key, _)| { + removed_chunks.insert(key); + }); } /// Insert the provided chunk into this state's terrain. diff --git a/voxygen/src/session.rs b/voxygen/src/session.rs index 668fb11901..2fdd831c1b 100644 --- a/voxygen/src/session.rs +++ b/voxygen/src/session.rs @@ -65,11 +65,6 @@ impl SessionState { .camera_mut() .set_fov_deg(global_state.settings.graphics.fov); let hud = Hud::new(global_state, &client.borrow()); - { - let mut client = client.borrow_mut(); - let my_entity = client.entity(); - client.state_mut().ecs_mut().insert(MyEntity(my_entity)); - } let voxygen_i18n = load_expect::(&i18n_asset_key( &global_state.settings.language.selected_language, )); @@ -101,9 +96,6 @@ impl SessionState { let mut client = self.client.borrow_mut(); for event in client.tick(self.inputs.clone(), dt, crate::ecs::sys::add_local_systems)? { - self.voxygen_i18n = load_expect::(&i18n_asset_key( - &global_state.settings.language.selected_language, - )); match event { client::Event::Chat(m) => { self.hud.new_message(m); @@ -183,7 +175,7 @@ impl PlayState for SessionState { } fn tick(&mut self, global_state: &mut GlobalState, events: Vec) -> PlayStateResult { - let localized_strings = load_expect::(&i18n_asset_key( + self.voxygen_i18n = load_expect::(&i18n_asset_key( &global_state.settings.language.selected_language, )); @@ -196,6 +188,17 @@ impl PlayState for SessionState { let client_state = self.client.borrow().get_client_state(); if let ClientState::Pending | ClientState::Character = client_state { + // Update MyEntity + // Note: Alternatively, the client could emit an event when the entity changes + // which may or may not be more elegant + { + let my_entity = self.client.borrow().entity(); + self.client + .borrow_mut() + .state_mut() + .ecs_mut() + .insert(MyEntity(my_entity)); + } // Compute camera data self.scene .camera_mut() @@ -642,7 +645,7 @@ impl PlayState for SessionState { Ok(TickAction::Disconnect) => return PlayStateResult::Pop, // Go to main menu Err(err) => { global_state.info_message = - Some(localized_strings.get("common.connection_lost").to_owned()); + Some(self.voxygen_i18n.get("common.connection_lost").to_owned()); error!("[session] Failed to tick the scene: {:?}", err); return PlayStateResult::Pop; @@ -705,7 +708,7 @@ impl PlayState for SessionState { // TODO: dont // Look for changes in the localization files if global_state.localization_watcher.reloaded() { - hud_events.push(HudEvent::ChangeLanguage(localized_strings.metadata.clone())); + hud_events.push(HudEvent::ChangeLanguage(self.voxygen_i18n.metadata.clone())); } // Maintain the UI. @@ -922,13 +925,13 @@ impl PlayState for SessionState { HudEvent::ChangeLanguage(new_language) => { global_state.settings.language.selected_language = new_language.language_identifier; - let localized_strings = load_watched::( + self.voxygen_i18n = load_watched::( &i18n_asset_key(&global_state.settings.language.selected_language), &mut global_state.localization_watcher, ) .unwrap(); - localized_strings.log_missing_entries(); - self.hud.update_language(localized_strings.clone()); + self.voxygen_i18n.log_missing_entries(); + self.hud.update_language(self.voxygen_i18n.clone()); }, HudEvent::ToggleFullscreen => { global_state From 70eba0c30ad389bd3a160fbc2dd2576aecb60eed Mon Sep 17 00:00:00 2001 From: Imbris Date: Fri, 7 Feb 2020 22:26:17 -0500 Subject: [PATCH 48/85] Update changelog --- CHANGELOG.md | 1 + Cargo.lock | 6 +++--- voxygen/Cargo.toml | 4 ++-- voxygen/src/lib.rs | 2 +- voxygen/src/main.rs | 14 +++++++------- voxygen/src/menu/char_selection/mod.rs | 14 -------------- voxygen/src/menu/main/mod.rs | 4 ++-- 7 files changed, 16 insertions(+), 29 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ae20e10bd..da34b44b5f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -71,6 +71,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Animals are more effective in combat - Pathfinding is much smoother and pets are cleverer - Animals run/turn at different speeds +- Updated winit 0.19 -> 0.22 ### Removed diff --git a/Cargo.lock b/Cargo.lock index 173829e19c..90ae730d02 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -581,7 +581,7 @@ dependencies = [ [[package]] name = "conrod_core" version = "0.63.0" -source = "git+https://gitlab.com/veloren/conrod.git?branch=hide_text#92925b122dfed139169c7d7687b3ca15c0cb5a2e" +source = "git+https://gitlab.com/veloren/conrod.git#971c7154f268ec52544634b5fde7383073b0a3b4" dependencies = [ "conrod_derive", "copypasta", @@ -596,7 +596,7 @@ dependencies = [ [[package]] name = "conrod_derive" version = "0.63.0" -source = "git+https://gitlab.com/veloren/conrod.git?branch=hide_text#92925b122dfed139169c7d7687b3ca15c0cb5a2e" +source = "git+https://gitlab.com/veloren/conrod.git#971c7154f268ec52544634b5fde7383073b0a3b4" dependencies = [ "proc-macro2 0.4.30", "quote 0.6.13", @@ -606,7 +606,7 @@ dependencies = [ [[package]] name = "conrod_winit" version = "0.63.0" -source = "git+https://gitlab.com/veloren/conrod.git?branch=hide_text#92925b122dfed139169c7d7687b3ca15c0cb5a2e" +source = "git+https://gitlab.com/veloren/conrod.git#971c7154f268ec52544634b5fde7383073b0a3b4" [[package]] name = "const-random" diff --git a/voxygen/Cargo.toml b/voxygen/Cargo.toml index 6f976137c6..7adb1ce97c 100644 --- a/voxygen/Cargo.toml +++ b/voxygen/Cargo.toml @@ -28,8 +28,8 @@ gfx_device_gl = { version = "0.16.2", optional = true } old_school_gfx_glutin_ext = { git = "https://github.com/Imberflur/old-school-gfx-glutin-ext.git" } glutin = "0.23.0" winit = { version = "0.21.0", features = ["serde"] } -conrod_core = { git = "https://gitlab.com/veloren/conrod.git", branch = "hide_text" } -conrod_winit = { git = "https://gitlab.com/veloren/conrod.git", branch = "hide_text" } +conrod_core = { git = "https://gitlab.com/veloren/conrod.git" } +conrod_winit = { git = "https://gitlab.com/veloren/conrod.git" } euc = { git = "https://github.com/zesterer/euc.git" } # ECS diff --git a/voxygen/src/lib.rs b/voxygen/src/lib.rs index c821a23ad5..ad05364972 100644 --- a/voxygen/src/lib.rs +++ b/voxygen/src/lib.rs @@ -49,7 +49,7 @@ pub struct GlobalState { #[cfg(feature = "singleplayer")] pub singleplayer: Option, // TODO: redo this so that the watcher doesn't have to exist for reloading to occur - localization_watcher: watch::ReloadIndicator, + pub localization_watcher: watch::ReloadIndicator, } impl GlobalState { diff --git a/voxygen/src/main.rs b/voxygen/src/main.rs index 4adf66812f..1820be77fd 100644 --- a/voxygen/src/main.rs +++ b/voxygen/src/main.rs @@ -7,19 +7,19 @@ use veloren_voxygen::{ audio::{self, AudioFrontend}, i18n::{self, i18n_asset_key, VoxygenLocalization}, logging, - menu::main::MainMenuState, profile::Profile, + run, settings::{AudioOutput, Settings}, window::Window, - Direction, GlobalState, PlayState, PlayStateResult, + GlobalState, }; use common::{ assets::{load_watched, watch}, clock::Clock, }; -use std::{mem, panic}; -use tracing::{debug, error, warn}; +use std::panic; +use tracing::{error, warn}; fn main() { #[cfg(feature = "tweak")] @@ -140,10 +140,10 @@ fn main() { &mut localization_watcher, ) .unwrap_or_else(|error| { - let preferred_language = &global_state.settings.language.selected_language; + let selected_language = &settings.language.selected_language; warn!( - ?e, - ?preferred_language, + ?error, + ?selected_language, "Impossible to load language: change to the default language (English) instead.", ); settings.language.selected_language = i18n::REFERENCE_LANG.to_owned(); diff --git a/voxygen/src/menu/char_selection/mod.rs b/voxygen/src/menu/char_selection/mod.rs index 3ed4305be5..ece5cb3ea4 100644 --- a/voxygen/src/menu/char_selection/mod.rs +++ b/voxygen/src/menu/char_selection/mod.rs @@ -114,20 +114,6 @@ impl PlayState for CharSelectionState { } } - let humanoid_body = self - .char_selection_ui - .get_character_list() - .and_then(|data| { - if let Some(character) = data.get(self.char_selection_ui.selected_character) { - match character.body { - comp::Body::Humanoid(body) => Some(body), - _ => None, - } - } else { - None - } - }); - let humanoid_body = self.get_humanoid_body(); let loadout = self.char_selection_ui.get_loadout(); diff --git a/voxygen/src/menu/main/mod.rs b/voxygen/src/menu/main/mod.rs index 5ea3ded5ab..1e2cc6763a 100644 --- a/voxygen/src/menu/main/mod.rs +++ b/voxygen/src/menu/main/mod.rs @@ -15,7 +15,6 @@ use ui::{Event as MainMenuEvent, MainMenuUi}; pub struct MainMenuState { main_menu_ui: MainMenuUi, - title_music_channel: Option, // Used for client creation. client_init: Option, } @@ -25,7 +24,6 @@ impl MainMenuState { pub fn new(global_state: &mut GlobalState) -> Self { Self { main_menu_ui: MainMenuUi::new(global_state), - title_music_channel: None, client_init: None, } } @@ -209,6 +207,8 @@ impl PlayState for MainMenuState { MainMenuEvent::StartSingleplayer => { let (singleplayer, server_settings) = Singleplayer::new(None); // TODO: Make client and server use the same thread pool + global_state.singleplayer = Some(singleplayer); + attempt_login( global_state, "singleplayer".to_owned(), From 5e8d74e00eca8532333fa7b4696eb055ac66c960 Mon Sep 17 00:00:00 2001 From: Imbris Date: Thu, 20 Feb 2020 18:41:51 -0500 Subject: [PATCH 49/85] fix Window::logical_size --- voxygen/src/ui/scale.rs | 14 +++++++------- voxygen/src/window.rs | 7 ++++++- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/voxygen/src/ui/scale.rs b/voxygen/src/ui/scale.rs index e946d2c570..01eb21afba 100644 --- a/voxygen/src/ui/scale.rs +++ b/voxygen/src/ui/scale.rs @@ -19,7 +19,7 @@ pub enum ScaleMode { pub struct Scale { mode: ScaleMode, // Current dpi factor - dpi_factor: f64, + scale_factor: f64, // Current logical window size window_dims: Vec2, } @@ -27,10 +27,10 @@ pub struct Scale { impl Scale { pub fn new(window: &Window, mode: ScaleMode) -> Self { let window_dims = window.logical_size(); - let dpi_factor = window.renderer().get_resolution().x as f64 / window_dims.x; + let scale_factor = window.renderer().get_resolution().x as f64 / window_dims.x; Scale { mode, - dpi_factor, + scale_factor, window_dims, } } @@ -54,7 +54,7 @@ impl Scale { // coordinates. pub fn scale_factor_logical(&self) -> f64 { match self.mode { - ScaleMode::Absolute(scale) => scale / self.dpi_factor, + ScaleMode::Absolute(scale) => scale / self.scale_factor, ScaleMode::DpiFactor => 1.0, ScaleMode::RelativeToWindow(dims) => { (self.window_dims.x / dims.x).min(self.window_dims.y / dims.y) @@ -64,11 +64,11 @@ impl Scale { // Calculate factor to transform between physical coordinates and our scaled // coordinates. - pub fn scale_factor_physical(&self) -> f64 { self.scale_factor_logical() * self.dpi_factor } + pub fn scale_factor_physical(&self) -> f64 { self.scale_factor_logical() * self.scale_factor } - // Updates internal window size (and/or dpi_factor). + // Updates internal window size (and/or scale_factor). pub fn window_resized(&mut self, new_dims: Vec2, renderer: &Renderer) { - self.dpi_factor = renderer.get_resolution().x as f64 / new_dims.x; + self.scale_factor = renderer.get_resolution().x as f64 / new_dims.x; self.window_dims = new_dims; } diff --git a/voxygen/src/window.rs b/voxygen/src/window.rs index f229b8683c..dce0d658f9 100644 --- a/voxygen/src/window.rs +++ b/voxygen/src/window.rs @@ -1031,7 +1031,12 @@ impl Window { #[allow(clippy::or_fun_call)] // TODO: Pending review in #587 pub fn logical_size(&self) -> Vec2 { - let (w, h) = self.window.window().inner_size().into(); + let (w, h) = self + .window + .window() + .inner_size() + .to_logical::(self.window.window().scale_factor()) + .into(); Vec2::new(w, h) } From 7dfb24d4a575399e45cf8746e23fb78bafe0a989 Mon Sep 17 00:00:00 2001 From: Imbris Date: Wed, 26 Feb 2020 01:06:37 -0500 Subject: [PATCH 50/85] Clean comments, fix mistake, change dep source now that compatible version is available on crates.io --- Cargo.lock | 3 ++- voxygen/Cargo.toml | 2 +- voxygen/src/lib.rs | 2 +- voxygen/src/main.rs | 2 +- voxygen/src/run.rs | 12 +----------- voxygen/src/session.rs | 1 - 6 files changed, 6 insertions(+), 16 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 90ae730d02..666abb157e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2779,7 +2779,8 @@ dependencies = [ [[package]] name = "old_school_gfx_glutin_ext" version = "0.23.0" -source = "git+https://github.com/Imberflur/old-school-gfx-glutin-ext.git#e128e187a81d1fa15409db38bc93e6677fc09288" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7cf32ddfed46b511150abb23d3193d38ec71ffafa561e6c2d82ad06eb51072e" dependencies = [ "gfx_core", "gfx_device_gl", diff --git a/voxygen/Cargo.toml b/voxygen/Cargo.toml index 7adb1ce97c..18488e16ae 100644 --- a/voxygen/Cargo.toml +++ b/voxygen/Cargo.toml @@ -25,7 +25,7 @@ anim = { package = "veloren-voxygen-anim", path = "src/anim", default-features = # Graphics gfx = "0.18.2" gfx_device_gl = { version = "0.16.2", optional = true } -old_school_gfx_glutin_ext = { git = "https://github.com/Imberflur/old-school-gfx-glutin-ext.git" } +old_school_gfx_glutin_ext = "0.23" glutin = "0.23.0" winit = { version = "0.21.0", features = ["serde"] } conrod_core = { git = "https://gitlab.com/veloren/conrod.git" } diff --git a/voxygen/src/lib.rs b/voxygen/src/lib.rs index ad05364972..058d61c901 100644 --- a/voxygen/src/lib.rs +++ b/voxygen/src/lib.rs @@ -63,6 +63,7 @@ impl GlobalState { pub fn maintain(&mut self, dt: f32) { self.audio.maintain(dt); } } +// TODO: appears to be currently unused by playstates pub enum Direction { Forwards, Backwards, @@ -86,7 +87,6 @@ pub enum PlayStateResult { /// A trait representing a playable game state. This may be a menu, a game /// session, the title screen, etc. pub trait PlayState { - /// Get a descriptive name for this state type. /// Called when entering this play state from another fn enter(&mut self, global_state: &mut GlobalState, direction: Direction); diff --git a/voxygen/src/main.rs b/voxygen/src/main.rs index 1820be77fd..6cc09a3406 100644 --- a/voxygen/src/main.rs +++ b/voxygen/src/main.rs @@ -15,7 +15,7 @@ use veloren_voxygen::{ }; use common::{ - assets::{load_watched, watch}, + assets::{load, load_expect, load_watched, watch}, clock::Clock, }; use std::panic; diff --git a/voxygen/src/run.rs b/voxygen/src/run.rs index 7105f15b2f..0898cbdeea 100644 --- a/voxygen/src/run.rs +++ b/voxygen/src/run.rs @@ -73,7 +73,7 @@ pub fn run(mut global_state: GlobalState, event_loop: EventLoop) { global_state.on_play_state_changed(); }); states.last_mut().map(|new_state| { - new_state.enter(&mut global_state, Direction::Forwards); + new_state.enter(&mut global_state, Direction::Backwards); }); }, PlayStateResult::Push(mut new_state) => { @@ -115,17 +115,7 @@ pub fn run(mut global_state: GlobalState, event_loop: EventLoop) { .swap_buffers() .expect("Failed to swap window buffers!"); } - //global_state.window.request_redraw(); }, - /*winit::event::Event::WindowEvent { - event: WindowEvent::RedrawRequested, - .. - } => { - // render here - } - .. - *control_flow = ControlFlow::Exit; - }*/ winit::event::Event::WindowEvent { event, .. } => global_state .window .handle_window_event(event, &mut global_state.settings), diff --git a/voxygen/src/session.rs b/voxygen/src/session.rs index 2fdd831c1b..456ce050a1 100644 --- a/voxygen/src/session.rs +++ b/voxygen/src/session.rs @@ -705,7 +705,6 @@ impl PlayState for SessionState { }, ); - // TODO: dont // Look for changes in the localization files if global_state.localization_watcher.reloaded() { hud_events.push(HudEvent::ChangeLanguage(self.voxygen_i18n.metadata.clone())); From 28e00a0f6eb96c1512d293c42921b4f0f779bfb4 Mon Sep 17 00:00:00 2001 From: Imbris Date: Sat, 7 Mar 2020 17:16:00 -0500 Subject: [PATCH 51/85] Fix singleplayer feature and rebase related stuff --- voxygen/src/hud/mod.rs | 38 ++++++++++++++++++-------------------- voxygen/src/lib.rs | 20 +++++++++++++++++++- voxygen/src/main.rs | 2 +- voxygen/src/session.rs | 8 ++------ 4 files changed, 40 insertions(+), 28 deletions(-) diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index 7a4c2a75f6..d776b04d84 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -465,17 +465,18 @@ impl Show { self.want_grab = true; // Unpause the game if we are on singleplayer - if let Some(singleplayer) = global_state.singleplayer.as_ref() { - singleplayer.pause(false); - }; + global_state.unpause(); } else { self.esc_menu = true; self.want_grab = false; - // Pause the game if we are on singleplayer - if let Some(singleplayer) = global_state.singleplayer.as_ref() { - singleplayer.pause(true); - }; + #[cfg(feature = "singleplayer")] + { + // Pause the game if we are on singleplayer + if let Some(singleplayer) = global_state.singleplayer.as_ref() { + singleplayer.pause(true); + } + } } } @@ -1720,10 +1721,13 @@ impl Hud { settings_window::Event::ToggleDebug => self.show.debug = !self.show.debug, settings_window::Event::ChangeTab(tab) => self.show.open_setting_tab(tab), settings_window::Event::Close => { - // Unpause the game if we are on singleplayer so that we can logout - if let Some(singleplayer) = global_state.singleplayer.as_ref() { - singleplayer.pause(false); - }; + #[cfg(feature = "singleplayer")] + { + // Unpause the game if we are on singleplayer so that we can logout + if let Some(singleplayer) = global_state.singleplayer.as_ref() { + singleplayer.pause(false); + }; + } self.show.settings(false) }, @@ -1908,24 +1912,18 @@ impl Hud { self.force_ungrab = false; // Unpause the game if we are on singleplayer - if let Some(singleplayer) = global_state.singleplayer.as_ref() { - singleplayer.pause(false); - }; + global_state.unpause(); }, Some(esc_menu::Event::Logout) => { // Unpause the game if we are on singleplayer so that we can logout - if let Some(singleplayer) = global_state.singleplayer.as_ref() { - singleplayer.pause(false); - }; + global_state.unpause(); events.push(Event::Logout); }, Some(esc_menu::Event::Quit) => events.push(Event::Quit), Some(esc_menu::Event::CharacterSelection) => { // Unpause the game if we are on singleplayer so that we can logout - if let Some(singleplayer) = global_state.singleplayer.as_ref() { - singleplayer.pause(false); - }; + global_state.unpause(); events.push(Event::CharacterSelection) }, diff --git a/voxygen/src/lib.rs b/voxygen/src/lib.rs index 058d61c901..202b7b55d3 100644 --- a/voxygen/src/lib.rs +++ b/voxygen/src/lib.rs @@ -28,12 +28,13 @@ pub mod window; // Reexports pub use crate::error::Error; +#[cfg(feature = "singleplayer")] +use crate::singleplayer::Singleplayer; use crate::{ audio::AudioFrontend, profile::Profile, render::Renderer, settings::Settings, - singleplayer::Singleplayer, window::{Event, Window}, }; use common::{assets::watch, clock::Clock}; @@ -61,6 +62,23 @@ impl GlobalState { } pub fn maintain(&mut self, dt: f32) { self.audio.maintain(dt); } + + #[cfg(feature = "singleplayer")] + pub fn paused(&self) -> bool { + self.singleplayer + .as_ref() + .map_or(false, Singleplayer::is_paused) + } + + #[cfg(not(feature = "singleplayer"))] + pub fn paused(&self) -> bool { false } + + pub fn unpause(&self) { + #[cfg(feature = "singleplayer")] + { + self.singleplayer.as_ref().map(|s| s.pause(false)); + } + } } // TODO: appears to be currently unused by playstates diff --git a/voxygen/src/main.rs b/voxygen/src/main.rs index 6cc09a3406..1820be77fd 100644 --- a/voxygen/src/main.rs +++ b/voxygen/src/main.rs @@ -15,7 +15,7 @@ use veloren_voxygen::{ }; use common::{ - assets::{load, load_expect, load_watched, watch}, + assets::{load_watched, watch}, clock::Clock, }; use std::panic; diff --git a/voxygen/src/session.rs b/voxygen/src/session.rs index 456ce050a1..bac1dbdf7a 100644 --- a/voxygen/src/session.rs +++ b/voxygen/src/session.rs @@ -636,9 +636,7 @@ impl PlayState for SessionState { self.inputs.climb = self.key_state.climb(); // Runs if either in a multiplayer server or the singleplayer server is unpaused - if global_state.singleplayer.is_none() - || !global_state.singleplayer.as_ref().unwrap().is_paused() - { + if !global_state.paused() { // Perform an in-game tick. match self.tick(global_state.clock.get_avg_delta(), global_state) { Ok(TickAction::Continue) => {}, // Do nothing @@ -986,9 +984,7 @@ impl PlayState for SessionState { }; // Runs if either in a multiplayer server or the singleplayer server is unpaused - if global_state.singleplayer.is_none() - || !global_state.singleplayer.as_ref().unwrap().is_paused() - { + if !global_state.paused() { self.scene.maintain( global_state.window.renderer_mut(), &mut global_state.audio, From 61983b1b06cc12bce9b65660df238ee370a61833 Mon Sep 17 00:00:00 2001 From: Imbris Date: Tue, 10 Mar 2020 02:35:25 -0400 Subject: [PATCH 52/85] poll twice --- voxygen/src/run.rs | 186 ++++++++++++++++++++++++--------------------- 1 file changed, 99 insertions(+), 87 deletions(-) diff --git a/voxygen/src/run.rs b/voxygen/src/run.rs index 0898cbdeea..65501fcbe3 100644 --- a/voxygen/src/run.rs +++ b/voxygen/src/run.rs @@ -16,6 +16,12 @@ pub fn run(mut global_state: GlobalState, event_loop: EventLoop) { debug!(?current_state, "Started game with state"); }); + // Used to ignore every other `MainEvenstCleared` + // This is a workaround for a bug on macos in which mouse motion events are only + // reported every other cycle of the event loop + // See: https://github.com/rust-windowing/winit/issues/1418 + let mut polled_twice = false; + event_loop.run(move |event, _, control_flow| { // Continously run loop since we handle sleeping *control_flow = winit::event_loop::ControlFlow::Poll; @@ -27,94 +33,10 @@ pub fn run(mut global_state: GlobalState, event_loop: EventLoop) { match event { winit::event::Event::MainEventsCleared => { - // Run tick here - - // What's going on here? - // --------------------- - // The state system used by Voxygen allows for the easy development of - // stack-based menus. For example, you may want a "title" state - // that can push a "main menu" state on top of it, which can in - // turn push a "settings" state or a "game session" state on top of it. - // The code below manages the state transfer logic automatically so that we - // don't have to re-engineer it for each menu we decide to add - // to the game. - let mut exit = true; - while let Some(state_result) = states.last_mut().map(|last| { - let events = global_state.window.fetch_events(); - last.tick(&mut global_state, events) - }) { - // Implement state transfer logic. - match state_result { - PlayStateResult::Continue => { - // Wait for the next tick. - global_state.clock.tick(Duration::from_millis( - 1000 / global_state.settings.graphics.max_fps as u64, - )); - - // Maintain global state. - global_state - .maintain(global_state.clock.get_last_delta().as_secs_f32()); - - exit = false; - break; - }, - PlayStateResult::Shutdown => { - debug!("Shutting down all states..."); - while states.last().is_some() { - states.pop().map(|old_state| { - debug!("Popped state '{}'.", old_state.name()); - global_state.on_play_state_changed(); - }); - } - }, - PlayStateResult::Pop => { - states.pop().map(|old_state| { - debug!("Popped state '{}'.", old_state.name()); - global_state.on_play_state_changed(); - }); - states.last_mut().map(|new_state| { - new_state.enter(&mut global_state, Direction::Backwards); - }); - }, - PlayStateResult::Push(mut new_state) => { - new_state.enter(&mut global_state, Direction::Forwards); - debug!("Pushed state '{}'.", new_state.name()); - states.push(new_state); - global_state.on_play_state_changed(); - }, - PlayStateResult::Switch(mut new_state) => { - new_state.enter(&mut global_state, Direction::Forwards); - states.last_mut().map(|old_state| { - debug!( - "Switching to state '{}' from state '{}'.", - new_state.name(), - old_state.name() - ); - mem::swap(old_state, &mut new_state); - global_state.on_play_state_changed(); - }); - }, - } - } - - if exit { - *control_flow = winit::event_loop::ControlFlow::Exit; - } - - // TODO: move - if let Some(last) = states.last_mut() { - global_state.window.renderer_mut().clear(); - last.render( - &mut global_state.window.renderer_mut(), - &global_state.settings, - ); - // Finish the frame. - global_state.window.renderer_mut().flush(); - global_state - .window - .swap_buffers() - .expect("Failed to swap window buffers!"); + if polled_twice { + handle_main_events_cleared(&mut states, control_flow, &mut global_state); } + polled_twice = !polled_twice; }, winit::event::Event::WindowEvent { event, .. } => global_state .window @@ -131,3 +53,93 @@ pub fn run(mut global_state: GlobalState, event_loop: EventLoop) { } }); } + +fn handle_main_events_cleared( + states: &mut Vec>, + control_flow: &mut winit::event_loop::ControlFlow, + global_state: &mut GlobalState, +) { + // Run tick here + + // What's going on here? + // --------------------- + // The state system used by Voxygen allows for the easy development of + // stack-based menus. For example, you may want a "title" state + // that can push a "main menu" state on top of it, which can in + // turn push a "settings" state or a "game session" state on top of it. + // The code below manages the state transfer logic automatically so that we + // don't have to re-engineer it for each menu we decide to add + // to the game. + let mut exit = true; + while let Some(state_result) = states.last_mut().map(|last| { + let events = global_state.window.fetch_events(); + last.tick(global_state, events) + }) { + // Implement state transfer logic. + match state_result { + PlayStateResult::Continue => { + // Wait for the next tick. + global_state.clock.tick(Duration::from_millis( + 1000 / global_state.settings.graphics.max_fps as u64, + )); + + // Maintain global state. + global_state.maintain(global_state.clock.get_last_delta().as_secs_f32()); + + exit = false; + break; + }, + PlayStateResult::Shutdown => { + debug!("Shutting down all states..."); + while states.last().is_some() { + states.pop().map(|old_state| { + debug!("Popped state '{}'.", old_state.name()); + global_state.on_play_state_changed(); + }); + } + }, + PlayStateResult::Pop => { + states.pop().map(|old_state| { + debug!("Popped state '{}'.", old_state.name()); + global_state.on_play_state_changed(); + }); + states.last_mut().map(|new_state| { + new_state.enter(global_state, Direction::Backwards); + }); + }, + PlayStateResult::Push(mut new_state) => { + new_state.enter(global_state, Direction::Forwards); + debug!("Pushed state '{}'.", new_state.name()); + states.push(new_state); + global_state.on_play_state_changed(); + }, + PlayStateResult::Switch(mut new_state) => { + new_state.enter(global_state, Direction::Forwards); + states.last_mut().map(|old_state| { + debug!( + "Switching to state '{}' from state '{}'.", + new_state.name(), + old_state.name() + ); + mem::swap(old_state, &mut new_state); + global_state.on_play_state_changed(); + }); + }, + } + } + + if exit { + *control_flow = winit::event_loop::ControlFlow::Exit; + } + + if let Some(last) = states.last_mut() { + global_state.window.renderer_mut().clear(); + last.render(global_state.window.renderer_mut(), &global_state.settings); + // Finish the frame. + global_state.window.renderer_mut().flush(); + global_state + .window + .swap_buffers() + .expect("Failed to swap window buffers!"); + } +} From 29c877cff3755b5436105747a761c81c95b0dd87 Mon Sep 17 00:00:00 2001 From: Imbris Date: Fri, 13 Mar 2020 00:05:06 -0400 Subject: [PATCH 53/85] Winit 0.22 released --- Cargo.lock | 136 +++++++++++++++++++++++++++++++++++++++------ voxygen/Cargo.toml | 6 +- 2 files changed, 122 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 666abb157e..5da6a34302 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -63,6 +63,12 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "000444226fcff248f2bc4c7625be32c63caccfecc2723a2b9f78a7487a49c407" +[[package]] +name = "android_log-sys" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8052e2d8aabbb8d556d6abbcce2a22b9590996c5f849b9c7ce4544a2e3b984e" + [[package]] name = "ansi_term" version = "0.11.0" @@ -578,6 +584,21 @@ dependencies = [ "objc", ] +[[package]] +name = "cocoa" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c49e86fc36d5704151f5996b7b3795385f50ce09e3be0f47a0cfde869681cf8" +dependencies = [ + "bitflags", + "block", + "core-foundation 0.7.0", + "core-graphics 0.19.2", + "foreign-types", + "libc", + "objc", +] + [[package]] name = "conrod_core" version = "0.63.0" @@ -1053,6 +1074,17 @@ dependencies = [ "byteorder 1.3.4", ] +[[package]] +name = "derivative" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb582b60359da160a9477ee80f15c8d784c477e69c217ef2cdd4169c24ea380f" +dependencies = [ + "proc-macro2 1.0.18", + "quote 1.0.7", + "syn 1.0.33", +] + [[package]] name = "deunicode" version = "1.1.1" @@ -1765,15 +1797,15 @@ dependencies = [ [[package]] name = "glutin" -version = "0.23.0" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf22d4e90c55d9be9f2ad52410e7a2c0d7e9c99d93a13df73a672e7ef4e8c7f7" +checksum = "9a9666c8fd9afd008f6559e2468c35e11aad1d110d525bb3b354e4138ec0e20f" dependencies = [ "android_glue", "cgl 0.3.2", - "cocoa 0.19.1", - "core-foundation 0.6.4", - "core-graphics 0.17.3", + "cocoa 0.20.2", + "core-foundation 0.7.0", + "core-graphics 0.19.2", "glutin_egl_sys", "glutin_emscripten_sys", "glutin_gles2_sys", @@ -1787,7 +1819,7 @@ dependencies = [ "parking_lot 0.10.2", "wayland-client 0.23.6", "winapi 0.3.8", - "winit 0.21.0", + "winit 0.22.2", ] [[package]] @@ -2170,6 +2202,12 @@ version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e" +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + [[package]] name = "jobserver" version = "0.1.21" @@ -2542,6 +2580,37 @@ dependencies = [ "winapi 0.3.8", ] +[[package]] +name = "ndk" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95a356cafe20aee088789830bfea3a61336e84ded9e545e00d3869ce95dcb80c" +dependencies = [ + "jni-sys", + "ndk-sys", + "num_enum", +] + +[[package]] +name = "ndk-glue" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1730ee2e3de41c3321160a6da815f008c4006d71b095880ea50e17cf52332b8" +dependencies = [ + "android_log-sys", + "lazy_static", + "libc", + "log", + "ndk", + "ndk-sys", +] + +[[package]] +name = "ndk-sys" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b2820aca934aba5ed91c79acc72b6a44048ceacc5d36c035ed4e051f12d887d" + [[package]] name = "net2" version = "0.2.34" @@ -2732,6 +2801,28 @@ dependencies = [ "libc", ] +[[package]] +name = "num_enum" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca565a7df06f3d4b485494f25ba05da1435950f4dc263440eda7a6fa9b8e36e4" +dependencies = [ + "derivative", + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffa5a33ddddfee04c0283a7653987d634e880347e96b5b2ed64de07efb59db9d" +dependencies = [ + "proc-macro-crate", + "proc-macro2 1.0.18", + "quote 1.0.7", + "syn 1.0.33", +] + [[package]] name = "objc" version = "0.2.7" @@ -2778,13 +2869,13 @@ dependencies = [ [[package]] name = "old_school_gfx_glutin_ext" -version = "0.23.0" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7cf32ddfed46b511150abb23d3193d38ec71ffafa561e6c2d82ad06eb51072e" +checksum = "a0557cea37cc48d238c938ded2873a6cc772704ee1eb01e832b43c2dd99624bc" dependencies = [ "gfx_core", "gfx_device_gl", - "glutin 0.23.0", + "glutin 0.24.1", ] [[package]] @@ -3062,6 +3153,15 @@ version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea" +[[package]] +name = "proc-macro-crate" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" +dependencies = [ + "toml", +] + [[package]] name = "proc-macro-error" version = "0.4.12" @@ -4621,7 +4721,7 @@ dependencies = [ "gilrs", "git2", "glsl-include", - "glutin 0.23.0", + "glutin 0.24.1", "guillotiere", "hashbrown", "image", @@ -4647,7 +4747,7 @@ dependencies = [ "veloren-server", "veloren-voxygen-anim", "veloren-world", - "winit 0.21.0", + "winit 0.22.2", "winres", ] @@ -5030,15 +5130,14 @@ dependencies = [ [[package]] name = "winit" -version = "0.21.0" +version = "0.22.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65a5c1a5ef76ac31cc97ad29489acdbed2178f3fc12ca00ee6cb11d60adb5a3a" +checksum = "1e4ccbf7ddb6627828eace16cacde80fc6bf4dbb3469f88487262a02cf8e7862" dependencies = [ - "android_glue", "bitflags", - "cocoa 0.19.1", - "core-foundation 0.6.4", - "core-graphics 0.17.3", + "cocoa 0.20.2", + "core-foundation 0.7.0", + "core-graphics 0.19.2", "core-video-sys", "dispatch 0.2.0", "instant", @@ -5047,6 +5146,9 @@ dependencies = [ "log", "mio", "mio-extras", + "ndk", + "ndk-glue", + "ndk-sys", "objc", "parking_lot 0.10.2", "percent-encoding 2.1.0", diff --git a/voxygen/Cargo.toml b/voxygen/Cargo.toml index 18488e16ae..b6b13df38f 100644 --- a/voxygen/Cargo.toml +++ b/voxygen/Cargo.toml @@ -25,9 +25,9 @@ anim = { package = "veloren-voxygen-anim", path = "src/anim", default-features = # Graphics gfx = "0.18.2" gfx_device_gl = { version = "0.16.2", optional = true } -old_school_gfx_glutin_ext = "0.23" -glutin = "0.23.0" -winit = { version = "0.21.0", features = ["serde"] } +old_school_gfx_glutin_ext = "0.24" +glutin = "0.24.0" +winit = { version = "0.22.2", features = ["serde"] } conrod_core = { git = "https://gitlab.com/veloren/conrod.git" } conrod_winit = { git = "https://gitlab.com/veloren/conrod.git" } euc = { git = "https://github.com/zesterer/euc.git" } From 8cd22074959df88a487c2b615e25d653708fc264 Mon Sep 17 00:00:00 2001 From: Imbris Date: Sun, 12 Jul 2020 23:11:48 -0400 Subject: [PATCH 54/85] Proper fix for winit / cpal conflict --- voxygen/src/window.rs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/voxygen/src/window.rs b/voxygen/src/window.rs index dce0d658f9..45ff254bf9 100644 --- a/voxygen/src/window.rs +++ b/voxygen/src/window.rs @@ -496,14 +496,19 @@ impl Window { let size = settings.graphics.window_size; - let win_builder = glutin::window::WindowBuilder::new() + let win_builder = winit::window::WindowBuilder::new() .with_title("Veloren") - .with_inner_size(glutin::dpi::LogicalSize::new( - size[0] as f64, - size[1] as f64, - )) + .with_inner_size(winit::dpi::LogicalSize::new(size[0] as f64, size[1] as f64)) .with_maximized(true); + // Avoid cpal / winit OleInitialize conflict + // See: https://github.com/rust-windowing/winit/pull/1524 + #[cfg(target_os = "windows")] + let win_builder = winit::platform::windows::WindowBuilderExtWindows::with_drag_and_drop( + win_builder, + false, + ); + let (window, device, factory, win_color_view, win_depth_view) = glutin::ContextBuilder::new() .with_gl(glutin::GlRequest::Specific(glutin::Api::OpenGl, (3, 2))) From f0d4ec32f55635c83d260b466f4e5600697d2f07 Mon Sep 17 00:00:00 2001 From: Imbris Date: Sun, 12 Jul 2020 23:30:46 -0400 Subject: [PATCH 55/85] clippy fixes --- voxygen/src/menu/main/mod.rs | 9 ++++----- voxygen/src/ui/event.rs | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/voxygen/src/menu/main/mod.rs b/voxygen/src/menu/main/mod.rs index 1e2cc6763a..24735fd95a 100644 --- a/voxygen/src/menu/main/mod.rs +++ b/voxygen/src/menu/main/mod.rs @@ -135,11 +135,10 @@ impl PlayState for MainMenuState { e ), // TODO: remove parentheses - client::AuthClientError::RequestError() => format!( - "{}", - localized_strings.get("main.login.failed_sending_request"), - ), - client::AuthClientError::ServerError(_, e) => format!("{}", e), + client::AuthClientError::RequestError() => localized_strings + .get("main.login.failed_sending_request") + .to_owned(), + client::AuthClientError::ServerError(_, e) => e, }, }, InitError::ClientCrashed => { diff --git a/voxygen/src/ui/event.rs b/voxygen/src/ui/event.rs index eed4160cb1..f44cb6ed91 100644 --- a/voxygen/src/ui/event.rs +++ b/voxygen/src/ui/event.rs @@ -26,7 +26,7 @@ impl Event { fn hidpi_factor(&self) -> f64 { winit::window::Window::scale_factor(&self.0) } } - convert_event!(event, &WindowRef(window.window())).map(|input| Self(input)) + convert_event!(event, &WindowRef(window.window())).map(Self) } pub fn is_keyboard_or_mouse(&self) -> bool { From a0d88e0774904168914d4e755499303ebf1fb818 Mon Sep 17 00:00:00 2001 From: Imbris Date: Mon, 13 Jul 2020 00:51:39 -0400 Subject: [PATCH 56/85] Fix mistake with event processing --- voxygen/src/ui/event.rs | 2 +- voxygen/src/window.rs | 14 ++++++-------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/voxygen/src/ui/event.rs b/voxygen/src/ui/event.rs index f44cb6ed91..e5a30b62cf 100644 --- a/voxygen/src/ui/event.rs +++ b/voxygen/src/ui/event.rs @@ -1,7 +1,7 @@ use conrod_core::{event::Input, input::Button}; use vek::*; -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct Event(pub Input); impl Event { pub fn try_from( diff --git a/voxygen/src/window.rs b/voxygen/src/window.rs index 45ff254bf9..b23efed7b5 100644 --- a/voxygen/src/window.rs +++ b/voxygen/src/window.rs @@ -241,7 +241,7 @@ pub enum AnalogGameInput { } /// Represents an incoming event from the window. -#[derive(Clone)] +#[derive(Clone, Debug)] pub enum Event { /// The window has been requested to close. Close, @@ -610,9 +610,7 @@ impl Window { #[allow(clippy::match_bool)] // TODO: Pending review in #587 pub fn fetch_events(&mut self) -> Vec { - let mut events = std::mem::take(&mut self.events); - - events.append(&mut self.supplement_events); + self.events.append(&mut self.supplement_events); // Refresh ui size (used when changing playstates) if self.needs_refresh_resize { @@ -622,9 +620,9 @@ impl Window { } // Receive any messages sent through the message channel - self.message_receiver - .try_iter() - .for_each(|message| events.push(Event::ScreenshotMessage(message))); + for message in self.message_receiver.try_iter() { + self.events.push(Event::ScreenshotMessage(message)) + } if let Some(gilrs) = &mut self.gilrs { while let Some(event) = gilrs.next_event() { @@ -779,7 +777,7 @@ impl Window { } } - let mut events = std::mem::replace(&mut self.events, Vec::new()); + let mut events = std::mem::take(&mut self.events); // Mouse emulation for the menus, to be removed when a proper menu navigation // system is available if !self.cursor_grabbed { From 7ab4f71b83e29ae2d49ea7e0fc7c9b8567209c08 Mon Sep 17 00:00:00 2001 From: Imbris Date: Mon, 13 Jul 2020 01:08:40 -0400 Subject: [PATCH 57/85] Windows cpal conflict fix not released yet so we need to use the git version --- Cargo.lock | 3 +-- Cargo.toml | 4 ++++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5da6a34302..ca9510ff72 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5131,8 +5131,7 @@ dependencies = [ [[package]] name = "winit" version = "0.22.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e4ccbf7ddb6627828eace16cacde80fc6bf4dbb3469f88487262a02cf8e7862" +source = "git+https://github.com/rust-windowing/winit.git?rev=6919c2fb2d45dc4063c9ef52a69d857ed30657c7#6919c2fb2d45dc4063c9ef52a69d857ed30657c7" dependencies = [ "bitflags", "cocoa 0.20.2", diff --git a/Cargo.toml b/Cargo.toml index 277aa6d984..78adb3641e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -72,3 +72,7 @@ debug = false [profile.releasedebuginfo] inherits = 'release' debug = 1 + +# cpal conflict fix isn't released yet +[patch.crates-io] +winit = { git = "https://github.com/rust-windowing/winit.git", rev = "6919c2fb2d45dc4063c9ef52a69d857ed30657c7" } From 97364d49ed757ede1a237e4f452facbeb0a39f13 Mon Sep 17 00:00:00 2001 From: Imbris Date: Mon, 13 Jul 2020 01:14:32 -0400 Subject: [PATCH 58/85] Increment glutin version in Cargo.toml file --- voxygen/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/voxygen/Cargo.toml b/voxygen/Cargo.toml index b6b13df38f..d0cf1c1234 100644 --- a/voxygen/Cargo.toml +++ b/voxygen/Cargo.toml @@ -26,7 +26,7 @@ anim = { package = "veloren-voxygen-anim", path = "src/anim", default-features = gfx = "0.18.2" gfx_device_gl = { version = "0.16.2", optional = true } old_school_gfx_glutin_ext = "0.24" -glutin = "0.24.0" +glutin = "0.24.1" winit = { version = "0.22.2", features = ["serde"] } conrod_core = { git = "https://gitlab.com/veloren/conrod.git" } conrod_winit = { git = "https://gitlab.com/veloren/conrod.git" } From 9bfb1b5f0035b039b2d82333c1ff95266632835f Mon Sep 17 00:00:00 2001 From: Imbris Date: Mon, 13 Jul 2020 19:36:19 -0400 Subject: [PATCH 59/85] Fix rebase mistake --- voxygen/src/session.rs | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/voxygen/src/session.rs b/voxygen/src/session.rs index bac1dbdf7a..21ede755ce 100644 --- a/voxygen/src/session.rs +++ b/voxygen/src/session.rs @@ -991,22 +991,6 @@ impl PlayState for SessionState { &scene_data, ); } - - let renderer = global_state.window.renderer_mut(); - // Clear the screen - renderer.clear(); - // Render the screen using the global renderer - self.scene.render( - renderer, - client.state(), - client.entity(), - client.get_tick(), - &scene_data, - ); - // Draw the UI to the screen - self.hud.render(renderer, self.scene.globals()); - // Finish the frame - renderer.flush(); } // Clean things up after the tick. From ede0cbdfd36069c48ccfb96c6a616e5d5645be32 Mon Sep 17 00:00:00 2001 From: Imbris Date: Tue, 14 Jul 2020 00:24:31 -0400 Subject: [PATCH 60/85] Get misc conrod fixes onto winit-20 branch, add diagnostic printlns --- Cargo.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ca9510ff72..8fd09e49e2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -602,7 +602,7 @@ dependencies = [ [[package]] name = "conrod_core" version = "0.63.0" -source = "git+https://gitlab.com/veloren/conrod.git#971c7154f268ec52544634b5fde7383073b0a3b4" +source = "git+https://gitlab.com/veloren/conrod.git#1ab6eccf94b16a8977a3274b31d4dbfef9cf9a30" dependencies = [ "conrod_derive", "copypasta", @@ -617,7 +617,7 @@ dependencies = [ [[package]] name = "conrod_derive" version = "0.63.0" -source = "git+https://gitlab.com/veloren/conrod.git#971c7154f268ec52544634b5fde7383073b0a3b4" +source = "git+https://gitlab.com/veloren/conrod.git#1ab6eccf94b16a8977a3274b31d4dbfef9cf9a30" dependencies = [ "proc-macro2 0.4.30", "quote 0.6.13", @@ -627,7 +627,7 @@ dependencies = [ [[package]] name = "conrod_winit" version = "0.63.0" -source = "git+https://gitlab.com/veloren/conrod.git#971c7154f268ec52544634b5fde7383073b0a3b4" +source = "git+https://gitlab.com/veloren/conrod.git#1ab6eccf94b16a8977a3274b31d4dbfef9cf9a30" [[package]] name = "const-random" From 27bfc74928f0af0b22eb84ece545f68587ee5d7f Mon Sep 17 00:00:00 2001 From: Imbris Date: Tue, 14 Jul 2020 00:40:01 -0400 Subject: [PATCH 61/85] small fix and refactor stop auto walk to a method --- voxygen/src/session.rs | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/voxygen/src/session.rs b/voxygen/src/session.rs index 21ede755ce..44602088fc 100644 --- a/voxygen/src/session.rs +++ b/voxygen/src/session.rs @@ -87,6 +87,12 @@ impl SessionState { is_aiming: false, } } + + fn stop_auto_walk(&mut self) { + self.auto_walk = false; + self.hud.auto_walk(false); + self.key_state.auto_walk = false; + } } impl SessionState { @@ -180,11 +186,6 @@ impl PlayState for SessionState { )); // TODO: can this be a method on the session or are there borrowcheck issues? - fn stop_auto_walk(auto_walk: &mut bool, key_state: &mut KeyState, hud: &mut Hud) { - *auto_walk = false; - hud.auto_walk(false); - key_state.auto_walk = false; - } let client_state = self.client.borrow().get_client_state(); if let ClientState::Pending | ClientState::Character = client_state { @@ -340,7 +341,7 @@ impl PlayState for SessionState { Event::InputUpdate(GameInput::Respawn, state) if state != self.key_state.respawn => { - stop_auto_walk(&mut auto_walk, &mut self.key_state, &mut self.hud); + self.stop_auto_walk(); self.key_state.respawn = state; if state { self.client.borrow_mut().respawn(); @@ -357,7 +358,7 @@ impl PlayState for SessionState { { self.key_state.toggle_sit = state; if state { - stop_auto_walk(&mut self.auto_walk, &mut self.key_state, &mut self.hud); + self.stop_auto_walk(); self.client.borrow_mut().toggle_sit(); } } @@ -366,31 +367,31 @@ impl PlayState for SessionState { { self.key_state.toggle_dance = state; if state { - stop_auto_walk(&mut self.auto_walk, &mut self.key_state, &mut self.hud); + self.stop_auto_walk(); self.client.borrow_mut().toggle_dance(); } } Event::InputUpdate(GameInput::MoveForward, state) => { if state && global_state.settings.gameplay.stop_auto_walk_on_input { - stop_auto_walk(&mut self.auto_walk, &mut self.key_state, &mut self.hud); + self.stop_auto_walk(); } self.key_state.up = state }, Event::InputUpdate(GameInput::MoveBack, state) => { if state && global_state.settings.gameplay.stop_auto_walk_on_input { - stop_auto_walk(&mut self.auto_walk, &mut self.key_state, &mut self.hud); + self.stop_auto_walk(); } self.key_state.down = state }, Event::InputUpdate(GameInput::MoveLeft, state) => { if state && global_state.settings.gameplay.stop_auto_walk_on_input { - stop_auto_walk(&mut self.auto_walk, &mut self.key_state, &mut self.hud); + self.stop_auto_walk(); } self.key_state.left = state }, Event::InputUpdate(GameInput::MoveRight, state) => { if state && global_state.settings.gameplay.stop_auto_walk_on_input { - stop_auto_walk(&mut self.auto_walk, &mut self.key_state, &mut self.hud); + self.stop_auto_walk(); } self.key_state.right = state }, From 2f5c8fa3f30d048b0daeba4e7328bd5afb848078 Mon Sep 17 00:00:00 2001 From: Imbris Date: Wed, 15 Jul 2020 00:56:50 -0400 Subject: [PATCH 62/85] Fixes for Alt key related issues --- voxygen/src/window.rs | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/voxygen/src/window.rs b/voxygen/src/window.rs index b23efed7b5..4f066dcf5a 100644 --- a/voxygen/src/window.rs +++ b/voxygen/src/window.rs @@ -475,6 +475,7 @@ pub struct Window { pub zoom_inversion: bool, pub mouse_y_inversion: bool, fullscreen: bool, + modifiers: winit::event::ModifiersState, needs_refresh_resize: bool, keypress_map: HashMap, pub remapping_keybindings: Option, @@ -578,6 +579,7 @@ impl Window { zoom_inversion: settings.gameplay.zoom_inversion, mouse_y_inversion: settings.gameplay.mouse_y_inversion, fullscreen: false, + modifiers: Default::default(), needs_refresh_resize: false, keypress_map, remapping_keybindings: None, @@ -915,7 +917,32 @@ impl Window { } self.events.push(Event::MouseButton(button, state)); }, - WindowEvent::KeyboardInput { input, .. } => { + WindowEvent::ModifiersChanged(modifiers) => self.modifiers = modifiers, + WindowEvent::KeyboardInput { + input, + is_synthetic, + .. + } => { + // Ignore synthetic tab presses so that we don't get tabs when alt-tabbing back + // into the window + if matches!( + input.virtual_keycode, + Some(winit::event::VirtualKeyCode::Tab) + ) && is_synthetic + { + return; + } + // Ignore Alt-F4 so we don't try to do anything heavy like take a screenshot + // when the window is about to close + if matches!(input, winit::event::KeyboardInput { + state: winit::event::ElementState::Pressed, + virtual_keycode: Some(winit::event::VirtualKeyCode::F4), + .. + }) && self.modifiers.alt() + { + return; + } + if let Some(key) = input.virtual_keycode { if let Some(game_inputs) = Window::map_input( KeyMouse::Key(key), From 0275cc9523243d62390922fb861d93d74fb6cc79 Mon Sep 17 00:00:00 2001 From: Imbris Date: Wed, 15 Jul 2020 19:19:11 -0400 Subject: [PATCH 63/85] Tweak pausing code to reduce duplication --- voxygen/src/hud/mod.rs | 20 ++++++++------------ voxygen/src/lib.rs | 11 +++++------ 2 files changed, 13 insertions(+), 18 deletions(-) diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index d776b04d84..a025369b75 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -465,18 +465,15 @@ impl Show { self.want_grab = true; // Unpause the game if we are on singleplayer + #[cfg(feature = "singleplayer")] global_state.unpause(); } else { self.esc_menu = true; self.want_grab = false; + // Pause the game if we are on singleplayer #[cfg(feature = "singleplayer")] - { - // Pause the game if we are on singleplayer - if let Some(singleplayer) = global_state.singleplayer.as_ref() { - singleplayer.pause(true); - } - } + global_state.pause(); } } @@ -1721,13 +1718,9 @@ impl Hud { settings_window::Event::ToggleDebug => self.show.debug = !self.show.debug, settings_window::Event::ChangeTab(tab) => self.show.open_setting_tab(tab), settings_window::Event::Close => { + // Unpause the game if we are on singleplayer so that we can logout #[cfg(feature = "singleplayer")] - { - // Unpause the game if we are on singleplayer so that we can logout - if let Some(singleplayer) = global_state.singleplayer.as_ref() { - singleplayer.pause(false); - }; - } + global_state.unpause(); self.show.settings(false) }, @@ -1912,10 +1905,12 @@ impl Hud { self.force_ungrab = false; // Unpause the game if we are on singleplayer + #[cfg(feature = "singleplayer")] global_state.unpause(); }, Some(esc_menu::Event::Logout) => { // Unpause the game if we are on singleplayer so that we can logout + #[cfg(feature = "singleplayer")] global_state.unpause(); events.push(Event::Logout); @@ -1923,6 +1918,7 @@ impl Hud { Some(esc_menu::Event::Quit) => events.push(Event::Quit), Some(esc_menu::Event::CharacterSelection) => { // Unpause the game if we are on singleplayer so that we can logout + #[cfg(feature = "singleplayer")] global_state.unpause(); events.push(Event::CharacterSelection) diff --git a/voxygen/src/lib.rs b/voxygen/src/lib.rs index 202b7b55d3..ccec393b41 100644 --- a/voxygen/src/lib.rs +++ b/voxygen/src/lib.rs @@ -73,12 +73,11 @@ impl GlobalState { #[cfg(not(feature = "singleplayer"))] pub fn paused(&self) -> bool { false } - pub fn unpause(&self) { - #[cfg(feature = "singleplayer")] - { - self.singleplayer.as_ref().map(|s| s.pause(false)); - } - } + #[cfg(feature = "singleplayer")] + pub fn unpause(&self) { self.singleplayer.as_ref().map(|s| s.pause(false)); } + + #[cfg(feature = "singleplayer")] + pub fn pause(&self) { self.singleplayer.as_ref().map(|s| s.pause(true)); } } // TODO: appears to be currently unused by playstates From 953773e1d30199fbf924f05db40d4ec954d159d8 Mon Sep 17 00:00:00 2001 From: Imbris Date: Thu, 16 Jul 2020 00:37:05 -0400 Subject: [PATCH 64/85] Temporarily remove voxygen character_renderer example until we move to a more actively maintained rendering backend --- Cargo.lock | 211 +++--------------------- voxygen/Cargo.toml | 1 - voxygen/examples/character_renderer.rs | 13 +- voxygen/examples/voxygen - Shortcut.lnk | Bin 1099 -> 0 bytes 4 files changed, 29 insertions(+), 196 deletions(-) delete mode 100644 voxygen/examples/voxygen - Shortcut.lnk diff --git a/Cargo.lock b/Cargo.lock index 8fd09e49e2..8f440b908e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -478,16 +478,6 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" -[[package]] -name = "cgl" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55e7ec0b74fe5897894cbc207092c577e87c52f8a59e8ca8d97ef37551f60a49" -dependencies = [ - "gleam", - "libc", -] - [[package]] name = "cgl" version = "0.3.2" @@ -554,21 +544,6 @@ dependencies = [ "bitflags", ] -[[package]] -name = "cocoa" -version = "0.18.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1706996401131526e36b3b49f0c4d912639ce110996f3ca144d78946727bce54" -dependencies = [ - "bitflags", - "block", - "core-foundation 0.6.4", - "core-graphics 0.17.3", - "foreign-types", - "libc", - "objc", -] - [[package]] name = "cocoa" version = "0.19.1" @@ -704,7 +679,7 @@ dependencies = [ "objc-foundation", "objc_id", "smithay-clipboard", - "wayland-client 0.23.6", + "wayland-client", "x11-clipboard", ] @@ -1604,17 +1579,6 @@ dependencies = [ "gl_generator 0.14.0", ] -[[package]] -name = "gfx_window_glutin" -version = "0.31.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "310ff66f08b5a55854b18fea2f48bdbb75c94458207ba574c9723be78e97a646" -dependencies = [ - "gfx_core", - "gfx_device_gl", - "glutin 0.21.2", -] - [[package]] name = "gilrs" version = "0.7.4" @@ -1720,15 +1684,6 @@ dependencies = [ "xml-rs", ] -[[package]] -name = "gleam" -version = "0.6.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cae10d7c99d0e77b4766e850a60898a17c1abaf01075531f1066f03dc7dc5fc5" -dependencies = [ - "gl_generator 0.13.1", -] - [[package]] name = "glib" version = "0.5.0" @@ -1769,32 +1724,6 @@ dependencies = [ "regex", ] -[[package]] -name = "glutin" -version = "0.21.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5371b35b309dace06be1b81b5f6adb1c9de578b7dbe1e74bf7e4ef762cf6febd" -dependencies = [ - "android_glue", - "cgl 0.2.3", - "cocoa 0.18.5", - "core-foundation 0.6.4", - "core-graphics 0.17.3", - "glutin_egl_sys", - "glutin_emscripten_sys", - "glutin_gles2_sys", - "glutin_glx_sys", - "glutin_wgl_sys", - "lazy_static", - "libloading 0.5.2", - "objc", - "osmesa-sys", - "parking_lot 0.9.0", - "wayland-client 0.21.13", - "winapi 0.3.8", - "winit 0.19.5", -] - [[package]] name = "glutin" version = "0.24.1" @@ -1802,7 +1731,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a9666c8fd9afd008f6559e2468c35e11aad1d110d525bb3b354e4138ec0e20f" dependencies = [ "android_glue", - "cgl 0.3.2", + "cgl", "cocoa 0.20.2", "core-foundation 0.7.0", "core-graphics 0.19.2", @@ -1817,9 +1746,9 @@ dependencies = [ "objc", "osmesa-sys", "parking_lot 0.10.2", - "wayland-client 0.23.6", + "wayland-client", "winapi 0.3.8", - "winit 0.22.2", + "winit", ] [[package]] @@ -2875,7 +2804,7 @@ checksum = "a0557cea37cc48d238c938ded2873a6cc772704ee1eb01e832b43c2dd99624bc" dependencies = [ "gfx_core", "gfx_device_gl", - "glutin 0.24.1", + "glutin", ] [[package]] @@ -3876,23 +3805,6 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7cb5678e1615754284ec264d9bb5b4c27d2018577fd90ac0ceb578591ed5ee4" -[[package]] -name = "smithay-client-toolkit" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ccb8c57049b2a34d2cc2b203fa785020ba0129d31920ef0d317430adaf748fa" -dependencies = [ - "andrew", - "bitflags", - "dlib", - "lazy_static", - "memmap", - "nix 0.14.1", - "wayland-client 0.21.13", - "wayland-commons 0.21.13", - "wayland-protocols 0.21.13", -] - [[package]] name = "smithay-client-toolkit" version = "0.6.6" @@ -3905,8 +3817,8 @@ dependencies = [ "lazy_static", "memmap", "nix 0.14.1", - "wayland-client 0.23.6", - "wayland-protocols 0.23.6", + "wayland-client", + "wayland-protocols", ] [[package]] @@ -3916,7 +3828,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "917e8ec7f535cd1a6cbf749c8866c24d67c548a80ac48c8e88a182eab5c07bd1" dependencies = [ "nix 0.14.1", - "smithay-client-toolkit 0.6.6", + "smithay-client-toolkit", ] [[package]] @@ -4717,11 +4629,10 @@ dependencies = [ "failure", "gfx", "gfx_device_gl", - "gfx_window_glutin", "gilrs", "git2", "glsl-include", - "glutin 0.24.1", + "glutin", "guillotiere", "hashbrown", "image", @@ -4747,7 +4658,7 @@ dependencies = [ "veloren-server", "veloren-voxygen-anim", "veloren-world", - "winit 0.22.2", + "winit", "winres", ] @@ -4912,21 +4823,6 @@ version = "0.2.63" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c9ba19973a58daf4db6f352eda73dc0e289493cd29fb2632eb172085b6521acd" -[[package]] -name = "wayland-client" -version = "0.21.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49963e5f9eeaf637bfcd1b9f0701c99fd5cd05225eb51035550d4272806f2713" -dependencies = [ - "bitflags", - "downcast-rs", - "libc", - "nix 0.14.1", - "wayland-commons 0.21.13", - "wayland-scanner 0.21.13", - "wayland-sys 0.21.13", -] - [[package]] name = "wayland-client" version = "0.23.6" @@ -4939,19 +4835,9 @@ dependencies = [ "libc", "mio", "nix 0.14.1", - "wayland-commons 0.23.6", - "wayland-scanner 0.23.6", - "wayland-sys 0.23.6", -] - -[[package]] -name = "wayland-commons" -version = "0.21.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40c08896768b667e1df195d88a62a53a2d1351a1ed96188be79c196b35bb32ec" -dependencies = [ - "nix 0.14.1", - "wayland-sys 0.21.13", + "wayland-commons", + "wayland-scanner", + "wayland-sys", ] [[package]] @@ -4961,20 +4847,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb66b0d1a27c39bbce712b6372131c6e25149f03ffb0cd017cf8f7de8d66dbdb" dependencies = [ "nix 0.14.1", - "wayland-sys 0.23.6", -] - -[[package]] -name = "wayland-protocols" -version = "0.21.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4afde2ea2a428eee6d7d2c8584fdbe8b82eee8b6c353e129a434cd6e07f42145" -dependencies = [ - "bitflags", - "wayland-client 0.21.13", - "wayland-commons 0.21.13", - "wayland-scanner 0.21.13", - "wayland-sys 0.21.13", + "wayland-sys", ] [[package]] @@ -4984,20 +4857,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6cc286643656742777d55dc8e70d144fa4699e426ca8e9d4ef454f4bf15ffcf9" dependencies = [ "bitflags", - "wayland-client 0.23.6", - "wayland-commons 0.23.6", - "wayland-scanner 0.23.6", -] - -[[package]] -name = "wayland-scanner" -version = "0.21.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf3828c568714507315ee425a9529edc4a4aa9901409e373e9e0027e7622b79e" -dependencies = [ - "proc-macro2 0.4.30", - "quote 0.6.13", - "xml-rs", + "wayland-client", + "wayland-commons", + "wayland-scanner", ] [[package]] @@ -5011,16 +4873,6 @@ dependencies = [ "xml-rs", ] -[[package]] -name = "wayland-sys" -version = "0.21.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520ab0fd578017a0ee2206623ba9ef4afe5e8f23ca7b42f6acfba2f4e66b1628" -dependencies = [ - "dlib", - "lazy_static", -] - [[package]] name = "wayland-sys" version = "0.23.6" @@ -5103,31 +4955,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "winit" -version = "0.19.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e96eb4bb472fa43e718e8fa4aef82f86cd9deac9483a1e1529230babdb394a8" -dependencies = [ - "android_glue", - "backtrace", - "bitflags", - "cocoa 0.18.5", - "core-foundation 0.6.4", - "core-graphics 0.17.3", - "lazy_static", - "libc", - "log", - "objc", - "parking_lot 0.9.0", - "percent-encoding 2.1.0", - "raw-window-handle", - "smithay-client-toolkit 0.4.6", - "wayland-client 0.21.13", - "winapi 0.3.8", - "x11-dl", -] - [[package]] name = "winit" version = "0.22.2" @@ -5153,8 +4980,8 @@ dependencies = [ "percent-encoding 2.1.0", "raw-window-handle", "serde", - "smithay-client-toolkit 0.6.6", - "wayland-client 0.23.6", + "smithay-client-toolkit", + "wayland-client", "winapi 0.3.8", "x11-dl", ] diff --git a/voxygen/Cargo.toml b/voxygen/Cargo.toml index d0cf1c1234..7576819aed 100644 --- a/voxygen/Cargo.toml +++ b/voxygen/Cargo.toml @@ -87,7 +87,6 @@ winres = "0.1" criterion = "0.3" git2 = "0.13" world = { package = "veloren-world", path = "../world" } -gfx_window_glutin = { version = "0.31.0", features = ["headless"] } [[bench]] name = "meshing_benchmark" diff --git a/voxygen/examples/character_renderer.rs b/voxygen/examples/character_renderer.rs index cf0aee4f53..a6d46d9b3c 100644 --- a/voxygen/examples/character_renderer.rs +++ b/voxygen/examples/character_renderer.rs @@ -1,9 +1,16 @@ -use common::comp; +// TODO: fix this example when we switch to actively maintained renderering +// backend Right now we would have to update `gfx_window_glutin` to work with +// the latest version of glutin or we would need to add headless support to +// `old_school_gfx_glutin_ext` + +fn main() { + println!("Example temporarily disabled, see the TODO comment for details"); +} +/*use common::comp; use gfx_window_glutin::init_headless; use vek::*; use veloren_voxygen::{render, scene::simple as scene}; -#[allow(clippy::clone_on_copy)] // TODO: Pending review in #587 fn main() { // Setup renderer let dim = (200u16, 300u16, 1, gfx::texture::AaMode::Single); @@ -72,4 +79,4 @@ fn main() { // Get image let img = renderer.create_screenshot().unwrap(); img.save("character.png").unwrap(); -} +}*/ diff --git a/voxygen/examples/voxygen - Shortcut.lnk b/voxygen/examples/voxygen - Shortcut.lnk deleted file mode 100644 index 722dd67f2a01eeb594c0e5744019c6c1ce4be9d2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1099 zcmbtSUucq16#sp;2aP>wne=}aBpaJfdx$g)mu}iNQqYZ{51O=3v-vMSXG#wh1yOA< zie7?^6{|rsC}`9k=0*>(pa+StRuLm2Y9rPjg6gHt^;4NKi0WQ`_uPB#Ilp`EIj0=} zr-K$j>rQ>TcK&gbj(l}k%&N%u@QaUAhlZbXo4(BKY}=JD zOEr8jmARFO_{F4<0QN$GD)KE|8A_WKWdweRNJ0R)LW9l<&Xf?2C4?w!f5n+1wlHGE zC?L8LYb>1!{sU_aX=D&AvjP{1eKTQIQR+$VCUg^6?wHl9-%&%TBUF|`i(1%W0gnzUIxD$I`&RT8 zS|CKl;^82@h#qQ*7~*tZ-XM%o8UYe40OQh*2aC>)%9~5FuD+p#JKIRVnwhWwjc=ky`83XR7f$rropabH_=)l^L*C z8VUdNDgL)4v2$h2>?OcVV8PteL>y!Nao4EJW*D)i`?iq&C^K*o#(r{d&+VCOo=opw M_hx2b1JjqrFV}A6p#T5? From c8133ded430cde86ca9638838ae82200f3537dda Mon Sep 17 00:00:00 2001 From: Imbris Date: Thu, 16 Jul 2020 20:14:34 -0400 Subject: [PATCH 65/85] Fix issue where client_init was not set to None after it finished, use tweaked version of winit to avoid macos link failure in the CI, small comment fixes --- CHANGELOG.md | 2 +- Cargo.lock | 2 +- Cargo.toml | 2 +- voxygen/examples/character_renderer.rs | 6 +++--- voxygen/src/menu/main/mod.rs | 1 + voxygen/src/run.rs | 2 +- 6 files changed, 8 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index da34b44b5f..7c54295dc3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -71,7 +71,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Animals are more effective in combat - Pathfinding is much smoother and pets are cleverer - Animals run/turn at different speeds -- Updated winit 0.19 -> 0.22 +- Updated windowing library (winit 0.19 -> 0.22) ### Removed diff --git a/Cargo.lock b/Cargo.lock index 8f440b908e..8b3401d012 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4958,7 +4958,7 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "winit" version = "0.22.2" -source = "git+https://github.com/rust-windowing/winit.git?rev=6919c2fb2d45dc4063c9ef52a69d857ed30657c7#6919c2fb2d45dc4063c9ef52a69d857ed30657c7" +source = "git+https://github.com/Imberflur/winit.git?branch=macos-test#e98133adf2abbfc4368f6c069d0beb2b8b688b42" dependencies = [ "bitflags", "cocoa 0.20.2", diff --git a/Cargo.toml b/Cargo.toml index 78adb3641e..b87d9807fd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -75,4 +75,4 @@ debug = 1 # cpal conflict fix isn't released yet [patch.crates-io] -winit = { git = "https://github.com/rust-windowing/winit.git", rev = "6919c2fb2d45dc4063c9ef52a69d857ed30657c7" } +winit = { git = "https://github.com/Imberflur/winit.git", branch = "macos-test" } diff --git a/voxygen/examples/character_renderer.rs b/voxygen/examples/character_renderer.rs index a6d46d9b3c..70cfe2a725 100644 --- a/voxygen/examples/character_renderer.rs +++ b/voxygen/examples/character_renderer.rs @@ -1,7 +1,7 @@ -// TODO: fix this example when we switch to actively maintained renderering -// backend Right now we would have to update `gfx_window_glutin` to work with +// TODO: Fix this example when we switch to actively maintained rendering +// backend. Right now we would have to update `gfx_window_glutin` to work with // the latest version of glutin or we would need to add headless support to -// `old_school_gfx_glutin_ext` +// `old_school_gfx_glutin_ext`. fn main() { println!("Example temporarily disabled, see the TODO comment for details"); diff --git a/voxygen/src/menu/main/mod.rs b/voxygen/src/menu/main/mod.rs index 24735fd95a..e520df7ecf 100644 --- a/voxygen/src/menu/main/mod.rs +++ b/voxygen/src/menu/main/mod.rs @@ -65,6 +65,7 @@ impl PlayState for MainMenuState { // Poll client creation. match self.client_init.as_ref().and_then(|init| init.poll()) { Some(InitMsg::Done(Ok(mut client))) => { + self.client_init = None; self.main_menu_ui.connected(); // Register voxygen components / resources crate::ecs::init(client.state_mut().ecs_mut()); diff --git a/voxygen/src/run.rs b/voxygen/src/run.rs index 65501fcbe3..b4f8856e17 100644 --- a/voxygen/src/run.rs +++ b/voxygen/src/run.rs @@ -16,7 +16,7 @@ pub fn run(mut global_state: GlobalState, event_loop: EventLoop) { debug!(?current_state, "Started game with state"); }); - // Used to ignore every other `MainEvenstCleared` + // Used to ignore every other `MainEventsCleared` // This is a workaround for a bug on macos in which mouse motion events are only // reported every other cycle of the event loop // See: https://github.com/rust-windowing/winit/issues/1418 From 2a9785a40c8991625db59432f357455ac273f15e Mon Sep 17 00:00:00 2001 From: Imbris Date: Fri, 17 Jul 2020 21:39:10 -0400 Subject: [PATCH 66/85] Remove supplement_events from Window, fix suppresed clippy lint, actually use exlusive fullscreen instead of a borderless window --- voxygen/src/window.rs | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/voxygen/src/window.rs b/voxygen/src/window.rs index 4f066dcf5a..c50dad3496 100644 --- a/voxygen/src/window.rs +++ b/voxygen/src/window.rs @@ -479,7 +479,6 @@ pub struct Window { needs_refresh_resize: bool, keypress_map: HashMap, pub remapping_keybindings: Option, - supplement_events: Vec, events: Vec, focused: bool, gilrs: Option, @@ -583,7 +582,6 @@ impl Window { needs_refresh_resize: false, keypress_map, remapping_keybindings: None, - supplement_events: Vec::new(), events: Vec::new(), focused: true, gilrs, @@ -610,10 +608,7 @@ impl Window { pub fn renderer_mut(&mut self) -> &mut Renderer { &mut self.renderer } - #[allow(clippy::match_bool)] // TODO: Pending review in #587 pub fn fetch_events(&mut self) -> Vec { - self.events.append(&mut self.supplement_events); - // Refresh ui size (used when changing playstates) if self.needs_refresh_resize { self.events @@ -686,13 +681,14 @@ impl Window { }, EventType::AxisChanged(axis, value, code) => { - let value = match self + let value = if self .controller_settings .inverted_axes .contains(&Axis::from((axis, code))) { - true => value * -1.0, - false => value, + -value + } else { + value }; let value = self @@ -1049,8 +1045,12 @@ impl Window { let window = self.window.window(); self.fullscreen = fullscreen; if fullscreen { - window.set_fullscreen(Some(winit::window::Fullscreen::Borderless( - window.current_monitor(), + window.set_fullscreen(Some(winit::window::Fullscreen::Exclusive( + window + .current_monitor() + .video_modes() + .max_by_key(|mode| mode.size().width) + .expect("No video modes available!!"), ))); } else { window.set_fullscreen(None); @@ -1081,8 +1081,6 @@ impl Window { pub fn send_event(&mut self, event: Event) { self.events.push(event) } - pub fn send_supplement_event(&mut self, event: Event) { self.supplement_events.push(event) } - pub fn take_screenshot(&mut self, settings: &Settings) { match self.renderer.create_screenshot() { Ok(img) => { From 5caa6a07b0eb5ec8a2c288b4a0a06109e71af2bb Mon Sep 17 00:00:00 2001 From: Nemanja Date: Sat, 18 Jul 2020 02:41:45 +0000 Subject: [PATCH 67/85] #612 Fixed utility items not being removed from inventory/hotbar --- common/src/comp/inventory/mod.rs | 126 +++++++++++++++++++++++++++ server/src/events/inventory_manip.rs | 5 +- 2 files changed, 128 insertions(+), 3 deletions(-) diff --git a/common/src/comp/inventory/mod.rs b/common/src/comp/inventory/mod.rs index 657386fec5..ca1c178395 100644 --- a/common/src/comp/inventory/mod.rs +++ b/common/src/comp/inventory/mod.rs @@ -241,6 +241,132 @@ impl Inventory { } } + /// Checks if inserting item exists in given cell. Inserts an item if it + /// exists. + pub fn insert_or_stack(&mut self, cell: usize, item: Item) -> Result, Item> { + match &item.kind { + ItemKind::Tool(_) | ItemKind::Armor { .. } | ItemKind::Lantern(_) => { + self.insert(cell, item) + }, + ItemKind::Utility { + amount: new_amount, .. + } => match self.slots.get_mut(cell) { + Some(Some(slot_item)) => { + if slot_item.name() == item.name() + && slot_item.description() == item.description() + { + if let Item { + kind: ItemKind::Utility { amount, .. }, + .. + } = slot_item + { + *amount += *new_amount; + self.recount_items(); + Ok(None) + } else { + let old_item = std::mem::replace(slot_item, item); + self.recount_items(); + Ok(Some(old_item)) + } + } else { + let old_item = std::mem::replace(slot_item, item); + self.recount_items(); + Ok(Some(old_item)) + } + }, + Some(None) => self.insert(cell, item), + None => Err(item), + }, + ItemKind::Ingredient { + amount: new_amount, .. + } => match self.slots.get_mut(cell) { + Some(Some(slot_item)) => { + if slot_item.name() == item.name() + && slot_item.description() == item.description() + { + if let Item { + kind: ItemKind::Ingredient { amount, .. }, + .. + } = slot_item + { + *amount += *new_amount; + self.recount_items(); + Ok(None) + } else { + let old_item = std::mem::replace(slot_item, item); + self.recount_items(); + Ok(Some(old_item)) + } + } else { + let old_item = std::mem::replace(slot_item, item); + self.recount_items(); + Ok(Some(old_item)) + } + }, + Some(None) => self.insert(cell, item), + None => Err(item), + }, + ItemKind::Consumable { + amount: new_amount, .. + } => match self.slots.get_mut(cell) { + Some(Some(slot_item)) => { + if slot_item.name() == item.name() + && slot_item.description() == item.description() + { + if let Item { + kind: ItemKind::Consumable { amount, .. }, + .. + } = slot_item + { + *amount += *new_amount; + self.recount_items(); + Ok(None) + } else { + let old_item = std::mem::replace(slot_item, item); + self.recount_items(); + Ok(Some(old_item)) + } + } else { + let old_item = std::mem::replace(slot_item, item); + self.recount_items(); + Ok(Some(old_item)) + } + }, + Some(None) => self.insert(cell, item), + None => Err(item), + }, + ItemKind::Throwable { + amount: new_amount, .. + } => match self.slots.get_mut(cell) { + Some(Some(slot_item)) => { + if slot_item.name() == item.name() + && slot_item.description() == item.description() + { + if let Item { + kind: ItemKind::Throwable { amount, .. }, + .. + } = slot_item + { + *amount += *new_amount; + self.recount_items(); + Ok(None) + } else { + let old_item = std::mem::replace(slot_item, item); + self.recount_items(); + Ok(Some(old_item)) + } + } else { + let old_item = std::mem::replace(slot_item, item); + self.recount_items(); + Ok(Some(old_item)) + } + }, + Some(None) => self.insert(cell, item), + None => Err(item), + }, + } + } + pub fn is_full(&self) -> bool { self.slots.iter().all(|slot| slot.is_some()) } /// O(n) count the number of items in this inventory. diff --git a/server/src/events/inventory_manip.rs b/server/src/events/inventory_manip.rs index b33a170e0b..0716eec9cc 100644 --- a/server/src/events/inventory_manip.rs +++ b/server/src/events/inventory_manip.rs @@ -240,14 +240,13 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv }; if reinsert { - let _ = inventory.insert(slot, item); + let _ = inventory.insert_or_stack(slot, item); } Some(comp::InventoryUpdateEvent::Used) }, _ => { - // TODO: this doesn't work for stackable items - inventory.insert(slot, item).unwrap(); + inventory.insert_or_stack(slot, item).unwrap(); None }, } From c7e8a6bfaea65edf05fa9b1d3fc36e529fd58ec5 Mon Sep 17 00:00:00 2001 From: Imbris Date: Sat, 18 Jul 2020 07:08:38 -0400 Subject: [PATCH 68/85] Remove gravity rather than reset velocity in unloaded chunks --- common/src/sys/phys.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/common/src/sys/phys.rs b/common/src/sys/phys.rs index 560adeb196..29242556d4 100644 --- a/common/src/sys/phys.rs +++ b/common/src/sys/phys.rs @@ -117,7 +117,12 @@ impl<'a> System<'a> for Sys { } else { 0.0 }); - let downward_force = if physics_state.in_fluid { + let in_loaded_chunk = terrain + .get_key(terrain.pos_key(pos.0.map(|e| e.floor() as i32))) + .is_some(); + let downward_force = if !in_loaded_chunk { + 0.0 // No gravity in unloaded chunks + } else if physics_state.in_fluid { (1.0 - BOUYANCY) * GRAVITY } else { GRAVITY @@ -125,16 +130,12 @@ impl<'a> System<'a> for Sys { vel.0 = integrate_forces(dt.0, vel.0, downward_force, friction); // Don't move if we're not in a loaded chunk - let mut pos_delta = if terrain - .get_key(terrain.pos_key(pos.0.map(|e| e.floor() as i32))) - .is_some() - { + let mut pos_delta = if in_loaded_chunk { // this is an approximation that allows most framerates to // behave in a similar manner. let dt_lerp = 0.2; (vel.0 * dt_lerp + old_vel.0 * (1.0 - dt_lerp)) * dt.0 } else { - vel.0 = Vec3::zero(); Vec3::zero() }; From d53ad7eb43c7d25b321a5453a480804be28f9f1a Mon Sep 17 00:00:00 2001 From: Imbris Date: Sat, 18 Jul 2020 08:17:42 -0400 Subject: [PATCH 69/85] Fix color depth selection --- voxygen/src/window.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/voxygen/src/window.rs b/voxygen/src/window.rs index c50dad3496..156b4ebcbd 100644 --- a/voxygen/src/window.rs +++ b/voxygen/src/window.rs @@ -1049,8 +1049,16 @@ impl Window { window .current_monitor() .video_modes() + .filter(|mode| mode.bit_depth() >= 24) .max_by_key(|mode| mode.size().width) - .expect("No video modes available!!"), + .unwrap_or_else(|| { + warn!("No video mode with a bit depth of at least 24 found"); + window + .current_monitor() + .video_modes() + .max_by_key(|mode| mode.size().width) + .expect("No video modes available!!") + }), ))); } else { window.set_fullscreen(None); From 14d500abbcc9fcb5f95350d6bdc0633e242f653e Mon Sep 17 00:00:00 2001 From: Imbris Date: Sat, 18 Jul 2020 12:21:22 -0400 Subject: [PATCH 70/85] Only screenshot and toggle fullscreen once per frame --- voxygen/src/run.rs | 4 ++++ voxygen/src/window.rs | 33 +++++++++++++++++++-------------- 2 files changed, 23 insertions(+), 14 deletions(-) diff --git a/voxygen/src/run.rs b/voxygen/src/run.rs index b4f8856e17..b738aeb511 100644 --- a/voxygen/src/run.rs +++ b/voxygen/src/run.rs @@ -59,6 +59,10 @@ fn handle_main_events_cleared( control_flow: &mut winit::event_loop::ControlFlow, global_state: &mut GlobalState, ) { + // Screenshot / Fullscreen toggle + global_state + .window + .resolve_deduplicated_events(&mut global_state.settings); // Run tick here // What's going on here? diff --git a/voxygen/src/window.rs b/voxygen/src/window.rs index c50dad3496..35c2d67b0c 100644 --- a/voxygen/src/window.rs +++ b/voxygen/src/window.rs @@ -488,6 +488,9 @@ pub struct Window { // Currently used to send and receive screenshot result messages message_sender: channel::Sender, message_receiver: channel::Receiver, + // Used for screenshots & fullscreen toggle to deduplicate/postpone to after event handler + take_screenshot: bool, + toggle_fullscreen: bool, } impl Window { @@ -591,6 +594,8 @@ impl Window { // Currently used to send and receive screenshot result messages message_sender, message_receiver, + take_screenshot: false, + toggle_fullscreen: false, }; this.fullscreen(settings.graphics.fullscreen); @@ -608,6 +613,18 @@ impl Window { pub fn renderer_mut(&mut self) -> &mut Renderer { &mut self.renderer } + pub fn resolve_deduplicated_events(&mut self, settings: &mut Settings) { + // Handle screenshots and toggling fullscreen + if self.take_screenshot { + self.take_screenshot = false; + self.take_screenshot(&settings); + } + if self.toggle_fullscreen { + self.toggle_fullscreen = false; + self.toggle_fullscreen(settings); + } + } + pub fn fetch_events(&mut self) -> Vec { // Refresh ui size (used when changing playstates) if self.needs_refresh_resize { @@ -876,10 +893,6 @@ impl Window { use winit::event::WindowEvent; let controls = &mut settings.controls; - // TODO: these used to be used to deduplicate events which they no longer do - // this needs to be handled elsewhere - let mut toggle_fullscreen = false; - let mut take_screenshot = false; match event { WindowEvent::CloseRequested => self.events.push(Event::Close), @@ -954,7 +967,7 @@ impl Window { GameInput::Fullscreen, ) { - toggle_fullscreen = !toggle_fullscreen; + self.toggle_fullscreen = !self.toggle_fullscreen; } Self::set_pressed( &mut self.keypress_map, @@ -963,7 +976,7 @@ impl Window { ); }, GameInput::Screenshot => { - take_screenshot = input.state + self.take_screenshot = input.state == winit::event::ElementState::Pressed && !Self::is_pressed( &mut self.keypress_map, @@ -993,14 +1006,6 @@ impl Window { }, _ => {}, } - - if take_screenshot { - self.take_screenshot(&settings); - } - - if toggle_fullscreen { - self.toggle_fullscreen(settings); - } } /// Moves cursor by an offset From e353bd3cfcf440311bc7b99083734688e12d0ef2 Mon Sep 17 00:00:00 2001 From: Monty Marz Date: Sat, 18 Jul 2020 21:56:45 +0000 Subject: [PATCH 71/85] Fix cut off chat --- voxygen/src/hud/chat.rs | 6 ++++-- voxygen/src/main.rs | 3 --- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/voxygen/src/hud/chat.rs b/voxygen/src/hud/chat.rs index 675f9622d6..46267014ad 100644 --- a/voxygen/src/hud/chat.rs +++ b/voxygen/src/hud/chat.rs @@ -30,6 +30,8 @@ widget_ids! { chat_icons[], } } +/*#[const_tweaker::tweak(min = 0.0, max = 60.0, step = 1.0)] +const X: f64 = 18.0;*/ const MAX_MESSAGES: usize = 100; @@ -316,7 +318,7 @@ impl<'a> Widget for Chat<'a> { .set(state.ids.message_box_bg, ui); let (mut items, _) = List::flow_down(state.messages.len() + 1) .top_left_with_margins_on(state.ids.message_box_bg, 0.0, 16.0) - .w_h(CHAT_BOX_WIDTH, CHAT_BOX_HEIGHT) + .w_h(CHAT_BOX_WIDTH - 16.0, CHAT_BOX_HEIGHT) .scroll_kids_vertically() .set(state.ids.message_box, ui); if state.ids.chat_icons.len() < state.messages.len() { @@ -337,7 +339,7 @@ impl<'a> Widget for Chat<'a> { let text = Text::new(&msg) .font_size(self.fonts.opensans.scale(15)) .font_id(self.fonts.opensans.conrod_id) - .w(CHAT_BOX_WIDTH - 16.0) + .w(CHAT_BOX_WIDTH - 17.0) .color(color) .line_spacing(2.0); // Add space between messages. diff --git a/voxygen/src/main.rs b/voxygen/src/main.rs index 1820be77fd..d119fdb788 100644 --- a/voxygen/src/main.rs +++ b/voxygen/src/main.rs @@ -22,9 +22,6 @@ use std::panic; use tracing::{error, warn}; fn main() { - #[cfg(feature = "tweak")] - const_tweaker::run().expect("Could not run server"); - // Load the settings // Note: This won't log anything due to it being called before // `logging::init`. The issue is we need to read a setting to decide From 51ff7cded2f1a175199a9509830fce51a1e6a937 Mon Sep 17 00:00:00 2001 From: Joshua Yanovski Date: Mon, 20 Jul 2020 02:08:19 +0200 Subject: [PATCH 72/85] Fix corrupted character inventories by adding protection. This is a temporary bandaid until we move to our new schema, but it should at least allow people to log in with old characters. --- .../down.sql | 1 + .../up.sql | 39 +++++++++++++++++++ server/src/persistence/mod.rs | 3 ++ 3 files changed, 43 insertions(+) create mode 100644 server/src/migrations/2020-07-19-223917_update_item_stats/down.sql create mode 100644 server/src/migrations/2020-07-19-223917_update_item_stats/up.sql diff --git a/server/src/migrations/2020-07-19-223917_update_item_stats/down.sql b/server/src/migrations/2020-07-19-223917_update_item_stats/down.sql new file mode 100644 index 0000000000..291a97c5ce --- /dev/null +++ b/server/src/migrations/2020-07-19-223917_update_item_stats/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/server/src/migrations/2020-07-19-223917_update_item_stats/up.sql b/server/src/migrations/2020-07-19-223917_update_item_stats/up.sql new file mode 100644 index 0000000000..a747646b1d --- /dev/null +++ b/server/src/migrations/2020-07-19-223917_update_item_stats/up.sql @@ -0,0 +1,39 @@ +-- This migration updates all "stats" fields for each armour item in player inventory. +UPDATE + inventory +SET + items = json_replace( + -- Replace inventory slots. + items, + '$.slots', + ( + -- Replace each item in the inventory, by splitting the json into an array, applying our changes, + -- and then re-aggregating. + -- + -- NOTE: SQLite does not seem to provide a way to guarantee the order is the same after aggregation! + -- For now, it *does* seem to order by slots.key, but this doesn't seem to be guaranteed by anything. + -- For explicitness, we still include the ORDER BY, even though it seems to have no effect. + SELECT json_group_array( + json_replace( + slots.value, + '$.kind.Armor.stats', + CASE + -- ONLY replace item stats when the stats field currently lacks "protection" + -- (NOTE: This will also return true if the value is null, so if you are creating a nullable + -- JSON field please be careful before rerunning this migration!). + WHEN json_extract(slots.value, '$.kind.Armor.stats.protection') IS NULL + THEN + -- Replace armor stats with new armor + json('{ "protection": { "Normal": 1.0 } }') + ELSE + -- The protection stat was already added. + json_extract(slots.value, '$.kind.Armor.stats') + END + ) + ) + -- Extract all item slots + FROM json_each(json_extract(items, '$.slots')) AS slots + ORDER BY slots.key + ) + ) +; diff --git a/server/src/persistence/mod.rs b/server/src/persistence/mod.rs index 34c1d4377a..441d741c63 100644 --- a/server/src/persistence/mod.rs +++ b/server/src/persistence/mod.rs @@ -22,6 +22,9 @@ use tracing::warn; // See: https://docs.rs/diesel_migrations/1.4.0/diesel_migrations/macro.embed_migrations.html // This macro is called at build-time, and produces the necessary migration info // for the `embedded_migrations` call below. +// +// NOTE: Adding a useless comment to trigger the migrations being run. Delete +// when needed. embed_migrations!(); /// Runs any pending database migrations. This is executed during server startup From 5b76ed23f44feed42b1425cadb9c2eb7460bf26a Mon Sep 17 00:00:00 2001 From: jshipsey Date: Mon, 20 Jul 2020 21:37:13 -0400 Subject: [PATCH 73/85] small fixes --- common/src/comp/ability.rs | 2 +- common/src/states/glide_wield.rs | 2 + common/src/states/wielding.rs | 6 + voxygen/src/anim/character/glidewield.rs | 304 ----------------------- voxygen/src/anim/src/character/run.rs | 2 +- voxygen/src/anim/src/character/stand.rs | 2 +- voxygen/src/scene/figure/mod.rs | 2 +- 7 files changed, 12 insertions(+), 308 deletions(-) delete mode 100644 voxygen/src/anim/character/glidewield.rs diff --git a/common/src/comp/ability.rs b/common/src/comp/ability.rs index b795222abe..575d83b8c5 100644 --- a/common/src/comp/ability.rs +++ b/common/src/comp/ability.rs @@ -259,7 +259,7 @@ impl From<&CharacterAbility> for CharacterState { }), CharacterAbility::BasicBlock => CharacterState::BasicBlock, CharacterAbility::Roll => CharacterState::Roll(roll::Data { - remaining_duration: Duration::from_millis(700), + remaining_duration: Duration::from_millis(500), was_wielded: false, // false by default. utils might set it to true }), CharacterAbility::TripleStrike { diff --git a/common/src/states/glide_wield.rs b/common/src/states/glide_wield.rs index 8efd033c75..e385a3d3a0 100644 --- a/common/src/states/glide_wield.rs +++ b/common/src/states/glide_wield.rs @@ -12,6 +12,8 @@ impl CharacterBehavior for Data { handle_move(&data, &mut update, 1.0); handle_jump(&data, &mut update); + handle_dodge_input(data, &mut update); + handle_wield(data, &mut update); // If not on the ground while wielding glider enter gliding state if !data.physics.on_ground && !data.physics.in_fluid { diff --git a/common/src/states/wielding.rs b/common/src/states/wielding.rs index c63f0a3d95..aa723fce2c 100644 --- a/common/src/states/wielding.rs +++ b/common/src/states/wielding.rs @@ -39,6 +39,12 @@ impl CharacterBehavior for Data { update } + fn glide_wield(&self, data: &JoinData) -> StateUpdate { + let mut update = StateUpdate::from(data); + attempt_glide_wield(data, &mut update); + update + } + fn swap_loadout(&self, data: &JoinData) -> StateUpdate { let mut update = StateUpdate::from(data); attempt_swap_loadout(data, &mut update); diff --git a/voxygen/src/anim/character/glidewield.rs b/voxygen/src/anim/character/glidewield.rs deleted file mode 100644 index 054dceda65..0000000000 --- a/voxygen/src/anim/character/glidewield.rs +++ /dev/null @@ -1,304 +0,0 @@ -use super::{super::Animation, CharacterSkeleton, SkeletonAttr}; -use common::comp::item::ToolKind; -use std::{f32::consts::PI, ops::Mul}; -use vek::*; - -pub struct GlideWieldAnimation; - -impl Animation for GlideWieldAnimation { - type Dependency = (Option, Option, Vec3, Vec3, Vec3, f64); - type Skeleton = CharacterSkeleton; - - - fn update_skeleton( - skeleton: &Self::Skeleton, - (_active_tool_kind, _second_tool_kind, velocity, orientation, last_ori, global_time): Self::Dependency, - anim_time: f64, - rate: &mut f32, - skeleton_attr: &SkeletonAttr, - ) -> Self::Skeleton { - let mut next = (*skeleton).clone(); - let speed = Vec2::::from(velocity).magnitude(); - *rate = 1.0; - let slow = (anim_time as f32 * 1.0).sin(); - let breathe = ((anim_time as f32 * 0.5).sin()).abs(); - let walkintensity = if speed > 5.0 { 1.0 } else { 0.45 }; - let walk = if speed > 5.0 { 1.0 } else { 0.5 }; - let lower = if speed > 5.0 { 0.0 } else { 1.0 }; - let _snapfoot = if speed > 5.0 { 1.1 } else { 2.0 }; - let lab = 1.0; - let foothoril = (anim_time as f32 * 16.0 * walk * lab as f32 + PI * 1.45).sin(); - let foothorir = (anim_time as f32 * 16.0 * walk * lab as f32 + PI * (0.45)).sin(); - - let footvertl = (anim_time as f32 * 16.0 * walk * lab as f32).sin(); - let footvertr = (anim_time as f32 * 16.0 * walk * lab as f32 + PI).sin(); - - let footrotl = (((5.0) - / (2.5 - + (2.5) - * ((anim_time as f32 * 16.0 * walk * lab as f32 + PI * 1.4).sin()) - .powf(2.0 as f32))) - .sqrt()) - * ((anim_time as f32 * 16.0 * walk * lab as f32 + PI * 1.4).sin()); - - let footrotr = (((5.0) - / (1.0 - + (4.0) - * ((anim_time as f32 * 16.0 * walk * lab as f32 + PI * 0.4).sin()) - .powf(2.0 as f32))) - .sqrt()) - * ((anim_time as f32 * 16.0 * walk * lab as f32 + PI * 0.4).sin()); - - let short = (((5.0) - / (1.5 - + 3.5 * ((anim_time as f32 * lab as f32 * 16.0 * walk).sin()).powf(2.0 as f32))) - .sqrt()) - * ((anim_time as f32 * lab as f32 * 16.0 * walk).sin()); - let noisea = (anim_time as f32 * 11.0 + PI / 6.0).sin(); - let noiseb = (anim_time as f32 * 19.0 + PI / 4.0).sin(); - - let shorte = (((5.0) - / (4.0 - + 1.0 * ((anim_time as f32 * lab as f32 * 16.0 * walk).sin()).powf(2.0 as f32))) - .sqrt()) - * ((anim_time as f32 * lab as f32 * 16.0 * walk).sin()); - - let shortalt = (anim_time as f32 * lab as f32 * 16.0 * walk + PI / 2.0).sin(); - let shortalter = (anim_time as f32 * lab as f32 * 16.0 * walk + PI / -2.0).sin(); - - let wave_stop = (anim_time as f32 * 26.0).min(PI / 2.0 / 2.0).sin(); - - let head_look = Vec2::new( - ((global_time + anim_time) as f32 / 18.0) - .floor() - .mul(7331.0) - .sin() - * 0.2, - ((global_time + anim_time) as f32 / 18.0) - .floor() - .mul(1337.0) - .sin() - * 0.1, - ); - - let ori = Vec2::from(orientation); - let last_ori = Vec2::from(last_ori); - let tilt = if Vec2::new(ori, last_ori) - .map(|o| Vec2::::from(o).magnitude_squared()) - .map(|m| m > 0.001 && m.is_finite()) - .reduce_and() - && ori.angle_between(last_ori).is_finite() - { - ori.angle_between(last_ori).min(0.2) - * last_ori.determine_side(Vec2::zero(), ori).signum() - } else { - 0.0 - } * 1.3; - - next.l_hand.offset = Vec3::new( - -2.0 - skeleton_attr.hand.0, - skeleton_attr.hand.1, - skeleton_attr.hand.2 + 15.0, - ); - next.l_hand.ori = Quaternion::rotation_x(3.35); - next.l_hand.scale = Vec3::one(); - - next.r_hand.offset = Vec3::new( - 2.0 + skeleton_attr.hand.0, - skeleton_attr.hand.1, - skeleton_attr.hand.2 + 15.0, - ); - next.r_hand.ori = Quaternion::rotation_x(3.35); - next.r_hand.scale = Vec3::one(); - - if speed > 0.5 { - next.head.offset = Vec3::new( - 0.0, - -3.0 + skeleton_attr.head.0, - skeleton_attr.head.1 + short * 0.1, - ); - next.head.ori = Quaternion::rotation_z(tilt * -2.5 + head_look.x * 0.2 - short * 0.1) - * Quaternion::rotation_x(head_look.y + 0.45 - lower * 0.35); - next.head.scale = Vec3::one() * skeleton_attr.head_scale; - - next.chest.offset = Vec3::new( - 0.0, - skeleton_attr.chest.0, - skeleton_attr.chest.1 + 2.0 + shortalt * -1.5 - lower, - ); - next.chest.ori = Quaternion::rotation_z(short * 0.10 * walkintensity + tilt * -1.0) - * Quaternion::rotation_y(tilt * 2.2) - * Quaternion::rotation_x( - shortalter * 0.035 + wave_stop * speed * -0.1 + (tilt.abs()), - ); - next.chest.scale = Vec3::one(); - - next.belt.offset = Vec3::new(0.0, skeleton_attr.belt.0, skeleton_attr.belt.1); - next.belt.ori = Quaternion::rotation_z(short * 0.1 + tilt * -1.1) - * Quaternion::rotation_y(tilt * 0.5); - next.belt.scale = Vec3::one(); - - next.glider.ori = Quaternion::rotation_x(0.8); - next.glider.offset = Vec3::new(0.0, -10.0, 15.0); - next.glider.scale = Vec3::one() * 1.0; - - next.back.offset = Vec3::new(0.0, skeleton_attr.back.0, skeleton_attr.back.1); - next.back.ori = - Quaternion::rotation_x(-0.25 + short * 0.1 + noisea * 0.1 + noiseb * 0.1); - next.back.scale = Vec3::one() * 1.02; - - next.shorts.offset = Vec3::new(0.0, skeleton_attr.shorts.0, skeleton_attr.shorts.1); - next.shorts.ori = Quaternion::rotation_z(short * 0.25 + tilt * -1.5) - * Quaternion::rotation_y(tilt * 0.7); - next.shorts.scale = Vec3::one(); - - next.l_foot.offset = Vec3::new( - -skeleton_attr.foot.0, - -1.5 + skeleton_attr.foot.1 + foothoril * -8.5 * walkintensity - lower * 1.0, - 2.0 + skeleton_attr.foot.2 + ((footvertl * -2.7).max(-1.0)) * walkintensity, - ); - next.l_foot.ori = Quaternion::rotation_x(-0.2 + footrotl * -1.2 * walkintensity) - * Quaternion::rotation_y(tilt * 1.8); - next.l_foot.scale = Vec3::one(); - - next.r_foot.offset = Vec3::new( - skeleton_attr.foot.0, - -1.5 + skeleton_attr.foot.1 + foothorir * -8.5 * walkintensity - lower * 1.0, - 2.0 + skeleton_attr.foot.2 + ((footvertr * -2.7).max(-1.0)) * walkintensity, - ); - next.r_foot.ori = Quaternion::rotation_x(-0.2 + footrotr * -1.2 * walkintensity) - * Quaternion::rotation_y(tilt * 1.8); - next.r_foot.scale = Vec3::one(); - - next.l_shoulder.offset = Vec3::new( - -skeleton_attr.shoulder.0, - skeleton_attr.shoulder.1, - skeleton_attr.shoulder.2, - ); - next.l_shoulder.ori = Quaternion::rotation_x(short * 0.15 * walkintensity); - next.l_shoulder.scale = Vec3::one() * 1.1; - - next.r_shoulder.offset = Vec3::new( - skeleton_attr.shoulder.0, - skeleton_attr.shoulder.1, - skeleton_attr.shoulder.2, - ); - next.r_shoulder.ori = Quaternion::rotation_x(short * -0.15 * walkintensity); - next.r_shoulder.scale = Vec3::one() * 1.1; - - next.main.offset = Vec3::new(-7.0, -6.5, 15.0); - next.main.ori = - Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57 + short * 0.25); - next.main.scale = Vec3::one(); - - next.second.scale = Vec3::one(); - - next.lantern.offset = Vec3::new( - skeleton_attr.lantern.0, - skeleton_attr.lantern.1, - skeleton_attr.lantern.2, - ); - next.lantern.ori = - Quaternion::rotation_x(shorte * 0.7 + 0.4) * Quaternion::rotation_y(shorte * 0.4); - next.lantern.scale = Vec3::one() * 0.65; - - next.torso.offset = Vec3::new(0.0, 0.0, 0.0) * skeleton_attr.scaler; - next.torso.ori = Quaternion::rotation_y(0.0); - next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; - - next.control.offset = Vec3::new(0.0, 0.0, 0.0); - next.control.ori = Quaternion::rotation_x(0.0); - next.control.scale = Vec3::one(); - - next.l_control.scale = Vec3::one(); - - next.r_control.scale = Vec3::one(); - } else { - next.head.offset = Vec3::new( - 0.0, - -3.0 + skeleton_attr.head.0, - skeleton_attr.head.1 + slow * 0.3 + breathe * -0.05, - ); - next.head.ori = - Quaternion::rotation_z(head_look.x) * Quaternion::rotation_x(head_look.y.abs()); - next.head.scale = Vec3::one() * skeleton_attr.head_scale + breathe * -0.05; - - next.chest.offset = Vec3::new( - 0.0, - skeleton_attr.chest.0, - skeleton_attr.chest.1 + slow * 0.3, - ); - next.chest.ori = Quaternion::rotation_z(head_look.x * 0.6); - next.chest.scale = Vec3::one() * 1.01 + breathe * 0.03; - - next.belt.offset = Vec3::new(0.0, skeleton_attr.belt.0, skeleton_attr.belt.1); - next.belt.ori = Quaternion::rotation_z(head_look.x * -0.1); - next.belt.scale = Vec3::one() + breathe * -0.03; - - next.glider.ori = Quaternion::rotation_x(0.35); - next.glider.offset = Vec3::new(0.0, -9.0, 17.0); - next.glider.scale = Vec3::one() * 1.0; - - next.back.offset = Vec3::new(0.0, skeleton_attr.back.0, skeleton_attr.back.1); - next.back.scale = Vec3::one() * 1.02; - - next.shorts.offset = Vec3::new(0.0, skeleton_attr.shorts.0, skeleton_attr.shorts.1); - next.shorts.ori = Quaternion::rotation_z(head_look.x * -0.2); - next.shorts.scale = Vec3::one() + breathe * -0.03; - - next.l_foot.offset = Vec3::new( - -skeleton_attr.foot.0, - skeleton_attr.foot.1, - skeleton_attr.foot.2, - ); - next.l_foot.scale = Vec3::one(); - - next.r_foot.offset = Vec3::new( - skeleton_attr.foot.0, - skeleton_attr.foot.1, - skeleton_attr.foot.2, - ); - next.r_foot.scale = Vec3::one(); - - next.l_shoulder.offset = Vec3::new( - -skeleton_attr.shoulder.0, - skeleton_attr.shoulder.1, - skeleton_attr.shoulder.2, - ); - next.l_shoulder.scale = (Vec3::one() + breathe * -0.05) * 1.15; - - next.r_shoulder.offset = Vec3::new( - skeleton_attr.shoulder.0, - skeleton_attr.shoulder.1, - skeleton_attr.shoulder.2, - ); - next.r_shoulder.scale = (Vec3::one() + breathe * -0.05) * 1.15; - - next.main.offset = Vec3::new(-7.0, -5.0, 15.0); - next.main.ori = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); - next.main.scale = Vec3::one(); - - next.second.offset = Vec3::new(0.0, 0.0, 0.0); - next.second.scale = Vec3::one(); - - next.lantern.offset = Vec3::new( - skeleton_attr.lantern.0, - skeleton_attr.lantern.1, - skeleton_attr.lantern.2, - ); - next.lantern.ori = Quaternion::rotation_x(0.1) * Quaternion::rotation_y(0.1); - next.lantern.scale = Vec3::one() * 0.65; - - next.torso.offset = Vec3::new(0.0, 0.0, 0.0) * skeleton_attr.scaler; - next.torso.ori = Quaternion::rotation_x(0.0); - next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; - - next.control.scale = Vec3::one(); - - next.l_control.scale = Vec3::one(); - - next.r_control.scale = Vec3::one(); - } - next - } -} diff --git a/voxygen/src/anim/src/character/run.rs b/voxygen/src/anim/src/character/run.rs index a2d24a2bd1..bb64344d39 100644 --- a/voxygen/src/anim/src/character/run.rs +++ b/voxygen/src/anim/src/character/run.rs @@ -35,7 +35,7 @@ impl Animation for RunAnimation { let speed = Vec2::::from(velocity).magnitude(); *rate = 1.0; - let impact = (avg_vel.z * 3000.0).max(-8.0); + let impact = (avg_vel.z).max(-8.0); let walkintensity = if speed > 5.0 { 1.0 } else { 0.45 }; let walk = if speed > 5.0 { 1.0 } else { 0.5 }; diff --git a/voxygen/src/anim/src/character/stand.rs b/voxygen/src/anim/src/character/stand.rs index a117e6c6b2..96bccada22 100644 --- a/voxygen/src/anim/src/character/stand.rs +++ b/voxygen/src/anim/src/character/stand.rs @@ -24,7 +24,7 @@ impl Animation for StandAnimation { let slow = (anim_time as f32 * 1.0).sin(); let breathe = ((anim_time as f32 * 0.5).sin()).abs(); - let impact = (avg_vel.z * 3000.0).max(-15.0); + let impact = (avg_vel.z).max(-15.0); let head_look = Vec2::new( ((global_time + anim_time) as f32 / 12.0) .floor() diff --git a/voxygen/src/scene/figure/mod.rs b/voxygen/src/scene/figure/mod.rs index 32db9276ba..5cbcf39661 100644 --- a/voxygen/src/scene/figure/mod.rs +++ b/voxygen/src/scene/figure/mod.rs @@ -2327,7 +2327,7 @@ impl FigureState { let smoothing = (5.0 * dt).min(1.0); if let Some(last_pos) = self.last_pos { - self.avg_vel = (1.0 - smoothing) * self.avg_vel + smoothing * (pos - last_pos) * dt; + self.avg_vel = (1.0 - smoothing) * self.avg_vel + smoothing * (pos - last_pos) / dt; } self.last_pos = Some(pos); } From fd9ec8fbd0ab6dfab671e036999afc5d67ced6a2 Mon Sep 17 00:00:00 2001 From: T-Dark0 Date: Tue, 21 Jul 2020 09:59:00 +0000 Subject: [PATCH 74/85] Voxygen and server-cli no longer panic if server creation returns error The process just exists instead --- server/src/error.rs | 13 +++++++++++++ server/src/lib.rs | 13 ++++++++++--- voxygen/src/menu/main/mod.rs | 16 ++++++++++++++++ voxygen/src/singleplayer.rs | 19 ++++++++++++++++--- 4 files changed, 55 insertions(+), 6 deletions(-) diff --git a/server/src/error.rs b/server/src/error.rs index a231b3ec34..74fadd4f74 100644 --- a/server/src/error.rs +++ b/server/src/error.rs @@ -1,5 +1,7 @@ use network::{NetworkError, ParticipantError, StreamError}; +use std::fmt::{self, Display}; + #[derive(Debug)] pub enum Error { NetworkErr(NetworkError), @@ -19,3 +21,14 @@ impl From for Error { impl From for Error { fn from(err: StreamError) -> Self { Error::StreamErr(err) } } + +impl Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Self::NetworkErr(err) => write!(f, "Network Error: {}", err), + Self::ParticipantErr(err) => write!(f, "Participant Error: {}", err), + Self::StreamErr(err) => write!(f, "Stream Error: {}", err), + Self::Other(err) => write!(f, "Error: {}", err), + } + } +} diff --git a/server/src/lib.rs b/server/src/lib.rs index dadab8890b..de784f3025 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -148,12 +148,19 @@ impl Server { Ok(vec) => vec, Err(error) => { tracing::warn!(?error, ?file, "Couldn't deserialize banned words file"); - return Err(Error::Other(error.to_string())); + return Err(Error::Other(format!( + "Couldn't read banned words file \"{}\"", + path.to_string_lossy() + ))); }, }, Err(error) => { - tracing::warn!(?error, ?path, "couldn't open banned words file"); - return Err(Error::Other(error.to_string())); + tracing::warn!(?error, ?path, "Couldn't open banned words file"); + return Err(Error::Other(format!( + "Couldn't open banned words file \"{}\". Error: {}", + path.to_string_lossy(), + error + ))); }, }; banned_words.append(&mut list); diff --git a/voxygen/src/menu/main/mod.rs b/voxygen/src/menu/main/mod.rs index e520df7ecf..c73883218d 100644 --- a/voxygen/src/menu/main/mod.rs +++ b/voxygen/src/menu/main/mod.rs @@ -50,6 +50,22 @@ impl PlayState for MainMenuState { &crate::i18n::i18n_asset_key(&global_state.settings.language.selected_language), ); + //Poll server creation + #[cfg(feature = "singleplayer")] + { + if let Some(singleplayer) = &global_state.singleplayer { + if let Ok(result) = singleplayer.receiver.try_recv() { + if let Err(error) = result { + tracing::error!(?error, "Could not start server"); + global_state.singleplayer = None; + self.client_init = None; + self.main_menu_ui.cancel_connection(); + self.main_menu_ui.show_info(format!("Error: {:?}", error)); + } + } + } + } + // Handle window events. for event in events { match event { diff --git a/voxygen/src/singleplayer.rs b/voxygen/src/singleplayer.rs index aee967caf4..ad67ed2ef3 100644 --- a/voxygen/src/singleplayer.rs +++ b/voxygen/src/singleplayer.rs @@ -1,7 +1,7 @@ use client::Client; use common::clock::Clock; -use crossbeam::channel::{unbounded, Receiver, Sender, TryRecvError}; -use server::{Event, Input, Server, ServerSettings}; +use crossbeam::channel::{bounded, unbounded, Receiver, Sender, TryRecvError}; +use server::{Error as ServerError, Event, Input, Server, ServerSettings}; use std::{ sync::{ atomic::{AtomicBool, Ordering}, @@ -23,6 +23,7 @@ enum Msg { pub struct Singleplayer { _server_thread: JoinHandle<()>, sender: Sender, + pub receiver: Receiver>, // Wether the server is stopped or not paused: Arc, } @@ -47,8 +48,19 @@ impl Singleplayer { let paused = Arc::new(AtomicBool::new(false)); let paused1 = paused.clone(); + let (result_sender, result_receiver) = bounded(1); + let thread = thread::spawn(move || { - let server = Server::new(settings2).expect("Failed to create server instance!"); + let server = match Server::new(settings2) { + Ok(server) => { + result_sender.send(Ok(())).unwrap(); + server + }, + Err(error) => { + result_sender.send(Err(error)).unwrap(); + return; + }, + }; let server = match thread_pool { Some(pool) => server.with_thread_pool(pool), @@ -62,6 +74,7 @@ impl Singleplayer { Singleplayer { _server_thread: thread, sender, + receiver: result_receiver, paused, }, settings, From dd581bc6c035e3f3943ca20e74db2e96aa03019e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=A4rtens?= Date: Thu, 16 Jul 2020 21:39:33 +0200 Subject: [PATCH 75/85] Participant closure was immeatiatly, even in case a new participant was connected, send a MSG and then dropped immeadiatly. The remote site should see it connect, be open for 1 single stream and read the message before it's notified that the participant is closed actually. This caused the faulure in one of our API tests (in lib, with client and server). Where it was possible that all messages were send and one side was dropped before the other side asked for the opened stream Also introduce better error detection in participant(and scheduler) by removing the std_async::Result and intruduce `Result<(),ParticipantError>` instead --- network/src/api.rs | 58 +++++++------------ network/src/participant.rs | 76 +++++++++++++++++-------- network/src/scheduler.rs | 9 +-- network/tests/closing.rs | 110 ++++++++++++++++++++++++++++++++++--- 4 files changed, 178 insertions(+), 75 deletions(-) diff --git a/network/src/api.rs b/network/src/api.rs index 33ce2de6ad..54de28cf35 100644 --- a/network/src/api.rs +++ b/network/src/api.rs @@ -54,7 +54,6 @@ pub struct Participant { remote_pid: Pid, a2b_stream_open_s: RwLock>, b2a_stream_opened_r: RwLock>, - closed: Arc>>, a2s_disconnect_s: A2sDisconnect, } @@ -78,9 +77,9 @@ pub struct Stream { mid: Mid, prio: Prio, promises: Promises, + send_closed: Arc, a2b_msg_s: crossbeam_channel::Sender<(Prio, Sid, OutgoingMessage)>, b2a_msg_recv_r: mpsc::UnboundedReceiver, - closed: Arc, a2b_close_stream_s: Option>, } @@ -427,14 +426,12 @@ impl Participant { a2b_stream_open_s: mpsc::UnboundedSender, b2a_stream_opened_r: mpsc::UnboundedReceiver, a2s_disconnect_s: mpsc::UnboundedSender<(Pid, S2bShutdownBparticipant)>, - closed: Arc>>, ) -> Self { Self { local_pid, remote_pid, a2b_stream_open_s: RwLock::new(a2b_stream_open_s), b2a_stream_opened_r: RwLock::new(b2a_stream_opened_r), - closed, a2s_disconnect_s: Arc::new(Mutex::new(Some(a2s_disconnect_s))), } } @@ -483,12 +480,14 @@ impl Participant { //use this lock for now to make sure that only one open at a time is made, // TODO: not sure if we can paralise that, check in future let mut a2b_stream_open_s = self.a2b_stream_open_s.write().await; - self.closed.read().await.clone()?; let (p2a_return_stream_s, p2a_return_stream_r) = oneshot::channel(); - a2b_stream_open_s + if let Err(e) = a2b_stream_open_s .send((prio, promises, p2a_return_stream_s)) .await - .unwrap(); + { + debug!(?e, "bParticipant is already closed, notifying"); + return Err(ParticipantError::ParticipantDisconnected); + } match p2a_return_stream_r.await { Ok(stream) => { let sid = stream.sid; @@ -497,8 +496,7 @@ impl Participant { }, Err(_) => { debug!(?self.remote_pid, "p2a_return_stream_r failed, closing participant"); - *self.closed.write().await = Err(ParticipantError::ProtocolFailedUnrecoverable); - Err(ParticipantError::ProtocolFailedUnrecoverable) + Err(ParticipantError::ParticipantDisconnected) }, } } @@ -540,7 +538,6 @@ impl Participant { //use this lock for now to make sure that only one open at a time is made, // TODO: not sure if we can paralise that, check in future let mut stream_opened_receiver = self.b2a_stream_opened_r.write().await; - self.closed.read().await.clone()?; match stream_opened_receiver.next().await { Some(stream) => { let sid = stream.sid; @@ -549,8 +546,7 @@ impl Participant { }, None => { debug!(?self.remote_pid, "stream_opened_receiver failed, closing participant"); - *self.closed.write().await = Err(ParticipantError::ProtocolFailedUnrecoverable); - Err(ParticipantError::ProtocolFailedUnrecoverable) + Err(ParticipantError::ParticipantDisconnected) }, } } @@ -602,11 +598,6 @@ impl Participant { // Remove, Close and try_unwrap error when unwrap fails! let pid = self.remote_pid; debug!(?pid, "Closing participant from network"); - { - let mut lock = self.closed.write().await; - lock.clone()?; - *lock = Err(ParticipantError::ParticipantDisconnected); - } //Streams will be closed by BParticipant match self.a2s_disconnect_s.lock().await.take() { @@ -619,17 +610,14 @@ impl Participant { .await .expect("Something is wrong in internal scheduler coding"); match finished_receiver.await { - Ok(Ok(())) => { - trace!(?pid, "Participant is now closed"); - Ok(()) - }, - Ok(Err(e)) => { - trace!( - ?e, - "Error occured during shutdown of participant and is propagated to \ - User" - ); - Err(ParticipantError::ProtocolFailedUnrecoverable) + Ok(res) => { + match res { + Ok(()) => trace!(?pid, "Participant is now closed"), + Err(ref e) => { + trace!(?pid, ?e, "Error occured during shutdown of participant") + }, + }; + res }, Err(e) => { //this is a bug. but as i am Participant i can't destroy the network @@ -664,9 +652,9 @@ impl Stream { sid: Sid, prio: Prio, promises: Promises, + send_closed: Arc, a2b_msg_s: crossbeam_channel::Sender<(Prio, Sid, OutgoingMessage)>, b2a_msg_recv_r: mpsc::UnboundedReceiver, - closed: Arc, a2b_close_stream_s: mpsc::UnboundedSender, ) -> Self { Self { @@ -675,9 +663,9 @@ impl Stream { mid: 0, prio, promises, + send_closed, a2b_msg_s, b2a_msg_recv_r, - closed, a2b_close_stream_s: Some(a2b_close_stream_s), } } @@ -788,10 +776,9 @@ impl Stream { /// [`send`]: Stream::send /// [`Participants`]: crate::api::Participant pub fn send_raw(&mut self, messagebuffer: Arc) -> Result<(), StreamError> { - if self.closed.load(Ordering::Relaxed) { + if self.send_closed.load(Ordering::Relaxed) { return Err(StreamError::StreamClosed); } - //debug!(?messagebuffer, "sending a message"); self.a2b_msg_s.send((self.prio, self.sid, OutgoingMessage { buffer: messagebuffer, cursor: 0, @@ -847,10 +834,7 @@ impl Stream { /// [`send_raw`]: Stream::send_raw /// [`recv`]: Stream::recv pub async fn recv_raw(&mut self) -> Result { - //no need to access self.closed here, as when this stream is closed the Channel - // is closed which will trigger a None let msg = self.b2a_msg_recv_r.next().await?; - //info!(?msg, "delivering a message"); Ok(msg.buffer) } } @@ -959,8 +943,8 @@ impl Drop for Participant { impl Drop for Stream { fn drop(&mut self) { - // a send if closed is unecessary but doesnt hurt, we must not crash here - if !self.closed.load(Ordering::Relaxed) { + // send if closed is unecessary but doesnt hurt, we must not crash + if !self.send_closed.load(Ordering::Relaxed) { let sid = self.sid; let pid = self.pid; debug!(?pid, ?sid, "Shutting down Stream"); diff --git a/network/src/participant.rs b/network/src/participant.rs index 8d6fa66aab..d2035b9989 100644 --- a/network/src/participant.rs +++ b/network/src/participant.rs @@ -28,7 +28,7 @@ use tracing::*; pub(crate) type A2bStreamOpen = (Prio, Promises, oneshot::Sender); pub(crate) type S2bCreateChannel = (Cid, Sid, Protocols, Vec<(Cid, Frame)>, oneshot::Sender<()>); -pub(crate) type S2bShutdownBparticipant = oneshot::Sender>; +pub(crate) type S2bShutdownBparticipant = oneshot::Sender>; pub(crate) type B2sPrioStatistic = (Pid, u64, u64); #[derive(Debug)] @@ -43,8 +43,8 @@ struct ChannelInfo { struct StreamInfo { prio: Prio, promises: Promises, + send_closed: Arc, b2a_msg_recv_s: mpsc::UnboundedSender, - closed: Arc, } #[derive(Debug)] @@ -57,6 +57,13 @@ struct ControlChannels { s2b_shutdown_bparticipant_r: oneshot::Receiver, /* own */ } +#[derive(Debug)] +struct ShutdownInfo { + //a2b_stream_open_r: mpsc::UnboundedReceiver, + b2a_stream_opened_s: mpsc::UnboundedSender, + error: Option, +} + #[derive(Debug)] pub struct BParticipant { remote_pid: Pid, @@ -64,12 +71,12 @@ pub struct BParticipant { offset_sid: Sid, channels: Arc>>, streams: RwLock>, - api_participant_closed: Arc>>, running_mgr: AtomicUsize, run_channels: Option, #[cfg(feature = "metrics")] metrics: Arc, no_channel_error_info: RwLock<(Instant, u64)>, + shutdown_info: RwLock, } impl BParticipant { @@ -84,7 +91,6 @@ impl BParticipant { mpsc::UnboundedReceiver, mpsc::UnboundedSender, oneshot::Sender, - Arc>>, ) { let (a2b_steam_open_s, a2b_stream_open_r) = mpsc::unbounded::(); let (b2a_stream_opened_s, b2a_stream_opened_r) = mpsc::unbounded::(); @@ -92,6 +98,12 @@ impl BParticipant { let (s2b_shutdown_bparticipant_s, s2b_shutdown_bparticipant_r) = oneshot::channel(); let (s2b_create_channel_s, s2b_create_channel_r) = mpsc::unbounded(); + let shutdown_info = RwLock::new(ShutdownInfo { + //a2b_stream_open_r: a2b_stream_open_r.clone(), + b2a_stream_opened_s: b2a_stream_opened_s.clone(), + error: None, + }); + let run_channels = Some(ControlChannels { a2b_stream_open_r, b2a_stream_opened_s, @@ -101,8 +113,6 @@ impl BParticipant { s2b_shutdown_bparticipant_r, }); - let api_participant_closed = Arc::new(RwLock::new(Ok(()))); - ( Self { remote_pid, @@ -110,18 +120,17 @@ impl BParticipant { offset_sid, channels: Arc::new(RwLock::new(vec![])), streams: RwLock::new(HashMap::new()), - api_participant_closed: api_participant_closed.clone(), running_mgr: AtomicUsize::new(0), run_channels, #[cfg(feature = "metrics")] metrics, no_channel_error_info: RwLock::new((Instant::now(), 0)), + shutdown_info, }, a2b_steam_open_s, b2a_stream_opened_r, s2b_create_channel_s, s2b_shutdown_bparticipant_s, - api_participant_closed, ) } @@ -269,7 +278,7 @@ impl BParticipant { will be closed, but not if we do channel-takeover" ); //TEMP FIX: as we dont have channel takeover yet drop the whole bParticipant - self.close_api(ParticipantError::ProtocolFailedUnrecoverable) + self.close_api(Some(ParticipantError::ProtocolFailedUnrecoverable)) .await; false } else { @@ -347,7 +356,8 @@ impl BParticipant { .streams_closed_total .with_label_values(&[&self.remote_pid_string]) .inc(); - si.closed.store(true, Ordering::Relaxed); + si.send_closed.store(true, Ordering::Relaxed); + si.b2a_msg_recv_s.close_channel(); trace!(?sid, "Closed stream from remote"); } else { warn!( @@ -411,7 +421,7 @@ impl BParticipant { }, Frame::Shutdown => { debug!("Shutdown received from remote side"); - self.close_api(ParticipantError::ParticipantDisconnected) + self.close_api(Some(ParticipantError::ParticipantDisconnected)) .await; }, f => unreachable!("Frame should never reache participant!: {:?}", f), @@ -495,6 +505,13 @@ impl BParticipant { _ = shutdown_open_mgr_receiver => None, } { debug!(?prio, ?promises, "Got request to open a new steam"); + //TODO: a2b_stream_open_r isn't closed on api_close yet. This needs to change. + //till then just check here if we are closed and in that case do nothing (not + // even answer) + if self.shutdown_info.read().await.error.is_some() { + continue; + } + let a2p_msg_s = a2p_msg_s.clone(); let sid = stream_ids; let stream = self @@ -538,10 +555,7 @@ impl BParticipant { trace!("Start participant_shutdown_mgr"); let sender = s2b_shutdown_bparticipant_r.await.unwrap(); - //Todo: isn't ParticipantDisconnected useless, as api is waiting rn for a - // callback? - self.close_api(ParticipantError::ParticipantDisconnected) - .await; + self.close_api(None).await; debug!("Closing all managers"); for sender in mgr_to_shutdown.drain(..) { @@ -580,7 +594,14 @@ impl BParticipant { self.metrics.participants_disconnected_total.inc(); debug!("BParticipant close done"); - sender.send(Ok(())).unwrap(); + let mut lock = self.shutdown_info.write().await; + sender + .send(match lock.error.take() { + None => Ok(()), + Some(e) => Err(e), + }) + .unwrap(); + trace!("Stop participant_shutdown_mgr"); self.running_mgr.fetch_sub(1, Ordering::Relaxed); } @@ -616,7 +637,8 @@ impl BParticipant { trace!(?sid, "Stopping api to use this stream"); match self.streams.read().await.get(&sid) { Some(si) => { - si.closed.store(true, Ordering::Relaxed); + si.send_closed.store(true, Ordering::Relaxed); + si.b2a_msg_recv_s.close_channel(); }, None => warn!("Couldn't find the stream, might be simulanious close from remote"), } @@ -658,12 +680,12 @@ impl BParticipant { a2b_close_stream_s: &mpsc::UnboundedSender, ) -> Stream { let (b2a_msg_recv_s, b2a_msg_recv_r) = mpsc::unbounded::(); - let closed = Arc::new(AtomicBool::new(false)); + let send_closed = Arc::new(AtomicBool::new(false)); self.streams.write().await.insert(sid, StreamInfo { prio, promises, + send_closed: send_closed.clone(), b2a_msg_recv_s, - closed: closed.clone(), }); #[cfg(feature = "metrics")] self.metrics @@ -675,20 +697,28 @@ impl BParticipant { sid, prio, promises, + send_closed, a2p_msg_s, b2a_msg_recv_r, - closed.clone(), a2b_close_stream_s.clone(), ) } /// close streams and set err - async fn close_api(&self, err: ParticipantError) { - *self.api_participant_closed.write().await = Err(err); + async fn close_api(&self, reason: Option) { + //closing api::Participant is done by closing all channels, exepct for the + // shutdown channel at this point! + let mut lock = self.shutdown_info.write().await; + if let Some(r) = reason { + lock.error = Some(r); + } + lock.b2a_stream_opened_s.close_channel(); + debug!("Closing all streams"); for (sid, si) in self.streams.write().await.drain() { trace!(?sid, "Shutting down Stream"); - si.closed.store(true, Ordering::Relaxed); + si.b2a_msg_recv_s.close_channel(); + si.send_closed.store(true, Ordering::Relaxed); } } } diff --git a/network/src/scheduler.rs b/network/src/scheduler.rs index b29818d1b1..000ccd92e7 100644 --- a/network/src/scheduler.rs +++ b/network/src/scheduler.rs @@ -3,7 +3,7 @@ use crate::metrics::NetworkMetrics; use crate::{ api::{Participant, ProtocolAddr}, channel::Handshake, - participant::{B2sPrioStatistic, BParticipant, S2bCreateChannel}, + participant::{B2sPrioStatistic, BParticipant, S2bCreateChannel, S2bShutdownBparticipant}, protocols::{Protocols, TcpProtocol, UdpProtocol}, types::Pid, }; @@ -45,13 +45,12 @@ use tracing_futures::Instrument; struct ParticipantInfo { secret: u128, s2b_create_channel_s: mpsc::UnboundedSender, - s2b_shutdown_bparticipant_s: - Option>>>, + s2b_shutdown_bparticipant_s: Option>, } type A2sListen = (ProtocolAddr, oneshot::Sender>); type A2sConnect = (ProtocolAddr, oneshot::Sender>); -type A2sDisconnect = (Pid, oneshot::Sender>); +type A2sDisconnect = (Pid, S2bShutdownBparticipant); #[derive(Debug)] struct ControlChannels { @@ -529,7 +528,6 @@ impl Scheduler { b2a_stream_opened_r, mut s2b_create_channel_s, s2b_shutdown_bparticipant_s, - api_participant_closed, ) = BParticipant::new( pid, sid, @@ -543,7 +541,6 @@ impl Scheduler { a2b_stream_open_s, b2a_stream_opened_r, participant_channels.a2s_disconnect_s, - api_participant_closed, ); #[cfg(feature = "metrics")] diff --git a/network/tests/closing.rs b/network/tests/closing.rs index 9b79f9c88a..1eb81b1990 100644 --- a/network/tests/closing.rs +++ b/network/tests/closing.rs @@ -20,7 +20,7 @@ use async_std::task; use task::block_on; -use veloren_network::StreamError; +use veloren_network::{Network, ParticipantError, Pid, StreamError, PROMISES_NONE}; mod helper; use helper::{network_participant_stream, tcp}; @@ -42,14 +42,10 @@ fn close_participant() { let (_n_a, p1_a, mut s1_a, _n_b, p1_b, mut s1_b) = block_on(network_participant_stream(tcp())); block_on(p1_a.disconnect()).unwrap(); - // The following will `Err`, but we don't know the exact error message. - // Why? because of the TCP layer we have no guarantee if the TCP messages send - // one line above already reached `p1_b`. If they reached them it would fail - // with a `ParticipantDisconnected` as a clean disconnect was performed. - // If they haven't reached them yet but will reach them during the execution it - // will return a unclean shutdown was detected. Nevertheless, if it returns - // Ok(()) then something is wrong! - assert!(block_on(p1_b.disconnect()).is_err()); + assert_eq!( + block_on(p1_b.disconnect()), + Err(ParticipantError::ParticipantDisconnected) + ); assert_eq!(s1_a.send("Hello World"), Err(StreamError::StreamClosed)); assert_eq!( @@ -229,3 +225,99 @@ fn close_network_then_disconnect_part() { assert!(block_on(p_a.disconnect()).is_err()); std::thread::sleep(std::time::Duration::from_millis(1000)); } + +#[test] +fn opened_stream_before_remote_part_is_closed() { + let (_, _) = helper::setup(false, 0); + let (_n_a, p_a, _, _n_b, p_b, _) = block_on(network_participant_stream(tcp())); + let mut s2_a = block_on(p_a.open(10, PROMISES_NONE)).unwrap(); + s2_a.send("HelloWorld").unwrap(); + let mut s2_b = block_on(p_b.opened()).unwrap(); + drop(p_a); + std::thread::sleep(std::time::Duration::from_millis(1000)); + assert_eq!(block_on(s2_b.recv()), Ok("HelloWorld".to_string())); +} + +#[test] +fn opened_stream_after_remote_part_is_closed() { + let (_, _) = helper::setup(false, 0); + let (_n_a, p_a, _, _n_b, p_b, _) = block_on(network_participant_stream(tcp())); + let mut s2_a = block_on(p_a.open(10, PROMISES_NONE)).unwrap(); + s2_a.send("HelloWorld").unwrap(); + drop(p_a); + std::thread::sleep(std::time::Duration::from_millis(1000)); + let mut s2_b = block_on(p_b.opened()).unwrap(); + assert_eq!(block_on(s2_b.recv()), Ok("HelloWorld".to_string())); + assert_eq!( + block_on(p_b.opened()).unwrap_err(), + ParticipantError::ParticipantDisconnected + ); +} + +#[test] +fn open_stream_after_remote_part_is_closed() { + let (_, _) = helper::setup(false, 0); + let (_n_a, p_a, _, _n_b, p_b, _) = block_on(network_participant_stream(tcp())); + let mut s2_a = block_on(p_a.open(10, PROMISES_NONE)).unwrap(); + s2_a.send("HelloWorld").unwrap(); + drop(p_a); + std::thread::sleep(std::time::Duration::from_millis(1000)); + let mut s2_b = block_on(p_b.opened()).unwrap(); + assert_eq!(block_on(s2_b.recv()), Ok("HelloWorld".to_string())); + assert_eq!( + block_on(p_b.open(20, PROMISES_NONE)).unwrap_err(), + ParticipantError::ParticipantDisconnected + ); +} + +#[test] +fn failed_stream_open_after_remote_part_is_closed() { + let (_, _) = helper::setup(false, 0); + let (_n_a, p_a, _, _n_b, p_b, _) = block_on(network_participant_stream(tcp())); + drop(p_a); + std::thread::sleep(std::time::Duration::from_millis(1000)); + assert_eq!( + block_on(p_b.opened()).unwrap_err(), + ParticipantError::ParticipantDisconnected + ); +} + +#[test] +fn open_participant_before_remote_part_is_closed() { + let (n_a, f) = Network::new(Pid::fake(1)); + std::thread::spawn(f); + let (n_b, f) = Network::new(Pid::fake(2)); + std::thread::spawn(f); + let addr = tcp(); + block_on(n_a.listen(addr.clone())).unwrap(); + let p_b = block_on(n_b.connect(addr)).unwrap(); + let mut s1_b = block_on(p_b.open(10, PROMISES_NONE)).unwrap(); + s1_b.send("HelloWorld").unwrap(); + let p_a = block_on(n_a.connected()).unwrap(); + drop(s1_b); + drop(p_b); + drop(n_b); + std::thread::sleep(std::time::Duration::from_millis(1000)); + let mut s1_a = block_on(p_a.opened()).unwrap(); + assert_eq!(block_on(s1_a.recv()), Ok("HelloWorld".to_string())); +} + +#[test] +fn open_participant_after_remote_part_is_closed() { + let (n_a, f) = Network::new(Pid::fake(1)); + std::thread::spawn(f); + let (n_b, f) = Network::new(Pid::fake(2)); + std::thread::spawn(f); + let addr = tcp(); + block_on(n_a.listen(addr.clone())).unwrap(); + let p_b = block_on(n_b.connect(addr)).unwrap(); + let mut s1_b = block_on(p_b.open(10, PROMISES_NONE)).unwrap(); + s1_b.send("HelloWorld").unwrap(); + drop(s1_b); + drop(p_b); + drop(n_b); + std::thread::sleep(std::time::Duration::from_millis(1000)); + let p_a = block_on(n_a.connected()).unwrap(); + let mut s1_a = block_on(p_a.opened()).unwrap(); + assert_eq!(block_on(s1_a.recv()), Ok("HelloWorld".to_string())); +} From 46b4c87cf383386b31d36836f2463f2957b478c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=A4rtens?= Date: Wed, 22 Jul 2020 09:50:26 +0200 Subject: [PATCH 76/85] fixed !688 --- server/src/lib.rs | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/server/src/lib.rs b/server/src/lib.rs index de784f3025..c37fdb9708 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -376,13 +376,7 @@ impl Server { let before_new_connections = Instant::now(); // 3) Handle inputs from clients - block_on(async { - //TIMEOUT 0.1 ms for msg handling - select!( - _ = Delay::new(std::time::Duration::from_micros(100)).fuse() => Ok(()), - err = self.handle_new_connections(&mut frontend_events).fuse() => err, - ) - })?; + block_on(self.handle_new_connections(&mut frontend_events))?; let before_message_system = Instant::now(); @@ -629,16 +623,29 @@ impl Server { &mut self, frontend_events: &mut Vec, ) -> Result<(), Error> { + //TIMEOUT 0.1 ms for msg handling + const TIMEOUT: Duration = Duration::from_micros(100); loop { - let participant = self.network.connected().await?; + let participant = match select!( + _ = Delay::new(TIMEOUT).fuse() => None, + pr = self.network.connected().fuse() => Some(pr), + ) { + None => return Ok(()), + Some(pr) => pr?, + }; debug!("New Participant connected to the server"); - let singleton_stream = match participant.opened().await { - Ok(s) => s, - Err(e) => { - warn!( - ?e, - "Failed to open a Stream from remote client. Dropping it" - ); + + let singleton_stream = match select!( + _ = Delay::new(TIMEOUT*100).fuse() => None, + sr = participant.opened().fuse() => Some(sr), + ) { + None => { + warn!("Either Slowloris attack or very slow client, dropping"); + return Ok(()); //return rather then continue to give removes a tick more to send data. + }, + Some(Ok(s)) => s, + Some(Err(e)) => { + warn!(?e, "Failed to open a Stream from remote client. dropping"); continue; }, }; From 31cfc051239a7924f152a9b3772e681cf9608c72 Mon Sep 17 00:00:00 2001 From: Monty Marz Date: Thu, 23 Jul 2020 12:10:13 +0000 Subject: [PATCH 77/85] lots of things animation fixes by slipped new cultist item settings option for loading screen tips loot table adjustments --- CHANGELOG.md | 1 + .../items/armor/back/dungeon_purple-0.ron | 12 +++++ assets/common/loot_table.ron | 25 +++------- .../voxygen/element/icons/debug_wand_m1.png | 4 +- .../voxygen/element/icons/debug_wand_m2.png | 4 +- assets/voxygen/element/icons/snake.png | 4 +- assets/voxygen/element/icons/snake_green.png | 3 ++ assets/voxygen/i18n/de_DE.ron | 23 +++++++-- assets/voxygen/i18n/en.ron | 30 +++++++++-- assets/voxygen/item_image_manifest.ron | 4 ++ .../voxygen/voxel/armor/back/dung_purp-0.vox | 3 ++ .../voxel/humanoid_armor_back_manifest.ron | 4 ++ common/src/comp/inventory/item/armor.rs | 3 +- server/src/sys/terrain.rs | 4 +- voxygen/src/anim/src/character/alpha.rs | 3 +- voxygen/src/anim/src/character/idle.rs | 3 ++ voxygen/src/hud/mod.rs | 4 ++ voxygen/src/hud/settings_window.rs | 32 ++++++++++-- voxygen/src/menu/main/ui.rs | 50 +++++++++++++++---- voxygen/src/session.rs | 4 ++ voxygen/src/settings.rs | 2 + world/src/site/dungeon/mod.rs | 3 ++ 22 files changed, 177 insertions(+), 48 deletions(-) create mode 100644 assets/common/items/armor/back/dungeon_purple-0.ron create mode 100644 assets/voxygen/element/icons/snake_green.png create mode 100644 assets/voxygen/voxel/armor/back/dung_purp-0.vox diff --git a/CHANGELOG.md b/CHANGELOG.md index 7c54295dc3..4b36a920b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -45,6 +45,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Minimap compass - Initial crafting system implementation - Protection stat to armor that reduces incoming damage +- Loading-Screen tips ### Changed diff --git a/assets/common/items/armor/back/dungeon_purple-0.ron b/assets/common/items/armor/back/dungeon_purple-0.ron new file mode 100644 index 0000000000..8e513848db --- /dev/null +++ b/assets/common/items/armor/back/dungeon_purple-0.ron @@ -0,0 +1,12 @@ +Item( + name: "Purple Cultist Cape", + description: "Smells like dark magic and candles.", + kind: Armor( + ( + kind: Back(DungPurp0), + stats: ( + protection: Normal(3.0), + ), + ) + ), +) diff --git a/assets/common/loot_table.ron b/assets/common/loot_table.ron index 2ba9b1da1d..fa609e55c6 100644 --- a/assets/common/loot_table.ron +++ b/assets/common/loot_table.ron @@ -61,9 +61,7 @@ // belts (0.17, "common.items.armor.belt.cloth_blue_0"), (0.17, "common.items.armor.belt.cloth_green_0"), - (0.17, "common.items.armor.belt.cloth_purple_0"), - (0.08, "common.items.armor.belt.cultist_belt"), - (0.08, "common.items.armor.belt.assassin"), + (0.17, "common.items.armor.belt.cloth_purple_0"), (0.08, "common.items.armor.belt.druid"), (0.06, "common.items.armor.belt.leather_0"), (0.06, "common.items.armor.belt.leather_2"), @@ -85,9 +83,7 @@ (0.025, "common.items.armor.chest.worker_red_0"), (0.025, "common.items.armor.chest.worker_red_1"), (0.025, "common.items.armor.chest.worker_yellow_0"), - (0.025, "common.items.armor.chest.worker_yellow_1"), - (0.08, "common.items.armor.chest.cultist_chest_purple"), - (0.08, "common.items.armor.chest.assassin"), + (0.025, "common.items.armor.chest.worker_yellow_1"), (0.08, "common.items.armor.chest.druid"), (0.06, "common.items.armor.chest.leather_0"), (0.06, "common.items.armor.chest.leather_2"), @@ -99,10 +95,7 @@ // shoes (0.15, "common.items.armor.foot.cloth_blue_0"), (0.15, "common.items.armor.foot.cloth_green_0"), - (0.15, "common.items.armor.foot.cloth_purple_0"), - (0.05, "common.items.armor.foot.jackalope_slippers"), - (0.08, "common.items.armor.foot.cultist_boots"), - (0.08, "common.items.armor.foot.assassin"), + (0.15, "common.items.armor.foot.cloth_purple_0"), (0.08, "common.items.armor.foot.druid"), (0.06, "common.items.armor.foot.leather_0"), (0.06, "common.items.armor.foot.leather_2"), @@ -115,9 +108,7 @@ (0.125, "common.items.armor.pants.cloth_blue_0"), (0.125, "common.items.armor.pants.cloth_green_0"), (0.125, "common.items.armor.pants.cloth_purple_0"), - (0.125, "common.items.armor.pants.worker_blue_0"), - (0.08, "common.items.armor.pants.cultist_legs_purple"), - (0.08, "common.items.armor.pants.assassin"), + (0.125, "common.items.armor.pants.worker_blue_0"), (0.08, "common.items.armor.pants.druid"), (0.04, "common.items.armor.pants.leather_0"), (0.04, "common.items.armor.pants.leather_2"), @@ -131,9 +122,7 @@ (0.125, "common.items.armor.shoulder.cloth_blue_0"), (0.125, "common.items.armor.shoulder.cloth_blue_1"), (0.125, "common.items.armor.shoulder.cloth_green_0"), - (0.125, "common.items.armor.shoulder.cloth_purple_0"), - (0.06, "common.items.armor.shoulder.cultist_shoulder_purple"), - (0.06, "common.items.armor.shoulder.assassin"), + (0.125, "common.items.armor.shoulder.cloth_purple_0"), (0.06, "common.items.armor.shoulder.druidshoulder"), (0.06, "common.items.armor.shoulder.leather_strips"), (0.04, "common.items.armor.shoulder.leather_0"), @@ -152,9 +141,7 @@ //gloves (0.17, "common.items.armor.hand.cloth_blue_0"), (0.17, "common.items.armor.hand.cloth_green_0"), - (0.17, "common.items.armor.hand.cloth_purple_0"), - (0.08, "common.items.armor.hand.cultist_hands_purple"), - (0.08, "common.items.armor.hand.assassin"), + (0.17, "common.items.armor.hand.cloth_purple_0"), (0.08, "common.items.armor.hand.druid"), (0.06, "common.items.armor.hand.leather_0"), (0.06, "common.items.armor.hand.leather_2"), diff --git a/assets/voxygen/element/icons/debug_wand_m1.png b/assets/voxygen/element/icons/debug_wand_m1.png index ae496eec91..87a12f1072 100644 --- a/assets/voxygen/element/icons/debug_wand_m1.png +++ b/assets/voxygen/element/icons/debug_wand_m1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ca52786d85797896195c075c1bc91c22f2c87d6a9e64f08dac65d3da3e91e48b -size 839 +oid sha256:d213bb2ce2a0ca2f845eb55934789945e77ff2e82a6ef11224dd8b489245ec0f +size 336 diff --git a/assets/voxygen/element/icons/debug_wand_m2.png b/assets/voxygen/element/icons/debug_wand_m2.png index c0b53ecf35..fb1e60ceb6 100644 --- a/assets/voxygen/element/icons/debug_wand_m2.png +++ b/assets/voxygen/element/icons/debug_wand_m2.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:50277d1222dd1b25ded80edc443830deb74db368a66dac5c0398112b9e4618cb -size 900 +oid sha256:239e6c33b6ea011bbfb82be7facb9e4aa974e12ac2c6f8d9018301680a36d982 +size 366 diff --git a/assets/voxygen/element/icons/snake.png b/assets/voxygen/element/icons/snake.png index 1459f2f094..fef32a8576 100644 --- a/assets/voxygen/element/icons/snake.png +++ b/assets/voxygen/element/icons/snake.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:514d65e989c1ea89f98cb4b45832213c13dc0c36906fd5bc6a8dae2ac9022e00 -size 2722 +oid sha256:bf7a20df6e3d39077dae3ddb3c29da886912c859f909ea63c33bf57201739bdd +size 532 diff --git a/assets/voxygen/element/icons/snake_green.png b/assets/voxygen/element/icons/snake_green.png new file mode 100644 index 0000000000..e05b6172e7 --- /dev/null +++ b/assets/voxygen/element/icons/snake_green.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:669f96ac170211741a50c4f1a2d1f21ee887e7eba4efeeb0ce30675daf2b9fd7 +size 564 diff --git a/assets/voxygen/i18n/de_DE.ron b/assets/voxygen/i18n/de_DE.ron index 2dccab239c..4258f0bcc2 100644 --- a/assets/voxygen/i18n/de_DE.ron +++ b/assets/voxygen/i18n/de_DE.ron @@ -408,8 +408,7 @@ magischen Gegenstände ergattern?"#, /// End chracter selection section /// Start character window section "character_window.character_name": "Charakter", - // Charater stats - // Charater stats + // Character stats "character_window.character_stats": r#"Ausdauer Beweglichkeit @@ -428,8 +427,26 @@ Willenskraft }, vector_map: { + "loading.tips": [ + "Drückt 'G', um Eure Laterne anzuzünden.", + "Drückt 'F1', um die Standard Tastenbelegung anzuzeigen.", + "Ihr könnt /say oder /s verwenden, um den Spielern in Eurer Nähe zu schreiben.", + "Verwendet /region oder /r, um mit Spielern in Eurem Gebiet zu schreiben.", + "Mit /tell könnt ihr einem Mitspieler direkte Nachrichten schicken.", + "NPCs mit demselben Level können unterschiedlich schwierig zu besiegen sein.", + "Behaltet den Boden um euch im Blick! Dort gibt es Nahrung, Kisten und Anderes zu finden.", + "Ist Euer Inventar voll mit Nahrung? Wertet es einfach durch Crafting auf!", + "Ihr sucht nach einem Abenteuer? Dungeons sind mit braunen Markierungen auf der Karte vermerkt!", + "Vergesst nicht Eure Grafikeinstellungen anzupassen! Mit 'N' kommt ihr in die Einstellungen.", + "Zusammen kämpfen macht mehr Spaß! Drückt 'O' um Eure Mitspieler anzuzeigen.", + "Ein NPC mit einem Schädel unter seiner Lebensanzeige ist deutlich stärker als Ihr.", + "Drückt 'J' um zu tanzen. Yeah!", + "Verwendet 'L-Shift' um mit Eurem Gleiter den Himmel zu erorbern!", + "Veloren befindet sich noch in der Pre-Alpha Phase. Wir tun unser Bestes, um das Spielgefühl jeden Tag zu verbessern!", + "Ihr wünscht, Euch mit uns auszutauschen oder wollt unserem Entwickler-Team beitreten? Kommt doch einfach auf unseren Discord-Server!", + ], "npc.speech.villager_under_attack": [ "Hilfe, ich werde angegriffen!", - ], + ], } ) diff --git a/assets/voxygen/i18n/en.ron b/assets/voxygen/i18n/en.ron index 7b5f5b2c4e..2aefbb6dcc 100644 --- a/assets/voxygen/i18n/en.ron +++ b/assets/voxygen/i18n/en.ron @@ -95,6 +95,7 @@ Is the client up to date?"#, /// Start Main screen section "main.connecting": "Connecting", "main.creating_world": "Creating world", + "main.tip": "Tip:", // Welcome notice that appears the first time Veloren is started "main.notice": r#"Welcome to the alpha version of Veloren! @@ -271,6 +272,7 @@ magically infused items?"#, "hud.settings.chat": "Chat", "hud.settings.background_transparency": "Background Transparency", "hud.settings.chat_character_name": "Character Names in chat", + "hud.settings.loading_tips": "Loading Screen Tips", "hud.settings.pan_sensitivity": "Pan Sensitivity", "hud.settings.zoom_sensitivity": "Zoom Sensitivity", @@ -408,7 +410,7 @@ magically infused items?"#, /// Start character window section "character_window.character_name": "Character Name", - // Charater stats + // Character stats "character_window.character_stats": r#"Endurance Fitness @@ -423,10 +425,30 @@ Protection /// Start Escape Menu Section "esc_menu.logout": "Logout", "esc_menu.quit_game": "Quit Game", - /// End Escape Menu Section + /// End Escape Menu Section + }, - vector_map: { + + vector_map: { + "loading.tips": [ + "Press 'G' to light your lantern.", + "Press 'F1' to see all default keybindings.", + "You can type /say or /s to only chat with players directly around you.", + "You can type /region or /r to only chat with players a couple of hundred blocks around you.", + "To send private message type /tell followed by a player name and your message.", + "NPCs with the same level can have a different difficulty.", + "Look at the ground for food, chests and other loot!", + "Inventory filled with food? Try crafting better food from it!", + "Wondering what's there to do? Dungeons are marked with brown spots on the map!", + "Don't forget to adjust the graphics for your system. Press 'N' to open the settings.", + "Playing with others is fun! Press 'O' to see who is online.", + "An NPC with a skull beneath their healthbar is quite powerful compared to yourself.", + "Press 'J' to dance. Party!", + "Press 'L-Shift' to open your Glider and conquer the skies.", + "Veloren is still in Pre-Alpha. We do our best to improve it every day!", + "If you want to join the Dev-Team or just have a chat with us join our Discord-Server.", + ], "npc.speech.villager_under_attack": [ "Help, I'm under attack!", "Help! I'm under attack!", @@ -507,6 +529,6 @@ Protection "Please don't do that again.", "Guards, throw this monster in the lake!", "I'll set my tarrasque on you!", - ], + ], } ) diff --git a/assets/voxygen/item_image_manifest.ron b/assets/voxygen/item_image_manifest.ron index af27a24813..5f6e0daa18 100644 --- a/assets/voxygen/item_image_manifest.ron +++ b/assets/voxygen/item_image_manifest.ron @@ -720,6 +720,10 @@ "voxel.armor.back.admin", (0.0, 0.0, 0.0), (-90.0, 180.0, 0.0), 1.0, ), + Armor(Back(DungPurp0)): VoxTrans( + "voxel.armor.back.dung_purp-0", + (0.0, 0.0, 0.0), (-90.0, 180.0, 0.0), 1.0, + ), // Rings Armor(Ring(Ring0)): Png( "element.icons.ring-0", diff --git a/assets/voxygen/voxel/armor/back/dung_purp-0.vox b/assets/voxygen/voxel/armor/back/dung_purp-0.vox new file mode 100644 index 0000000000..1c82139479 --- /dev/null +++ b/assets/voxygen/voxel/armor/back/dung_purp-0.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6589aee7dcf964a185725824c2fea58e419d734230e5d22fcec789a8a20e5224 +size 1672 diff --git a/assets/voxygen/voxel/humanoid_armor_back_manifest.ron b/assets/voxygen/voxel/humanoid_armor_back_manifest.ron index 5165820bc9..2cd68d083e 100644 --- a/assets/voxygen/voxel/humanoid_armor_back_manifest.ron +++ b/assets/voxygen/voxel/humanoid_armor_back_manifest.ron @@ -11,6 +11,10 @@ Admin: ( vox_spec: ("armor.back.admin", (-5.0, -1.0, -11.0)), color: None + ), + DungPurp0: ( + vox_spec: ("armor.back.dung_purp-0", (-5.0, -1.0, -14.0)), + color: None ), }, )) diff --git a/common/src/comp/inventory/item/armor.rs b/common/src/comp/inventory/item/armor.rs index f9df886468..0b8f5ff3fc 100644 --- a/common/src/comp/inventory/item/armor.rs +++ b/common/src/comp/inventory/item/armor.rs @@ -306,8 +306,9 @@ pub const ALL_SHOULDERS: [Shoulder; 24] = [ pub enum Back { Short0 = 1, Admin = 2, + DungPurp0 = 3, } -pub const ALL_BACKS: [Back; 2] = [Back::Short0, Back::Admin]; +pub const ALL_BACKS: [Back; 3] = [Back::Short0, Back::Admin, Back::DungPurp0]; #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] #[repr(u32)] pub enum Ring { diff --git a/server/src/sys/terrain.rs b/server/src/sys/terrain.rs index 4c2e9e976f..ed5acff8df 100644 --- a/server/src/sys/terrain.rs +++ b/server/src/sys/terrain.rs @@ -208,7 +208,9 @@ impl<'a> System<'a> for Sys { foot: Some(assets::load_expect_cloned( "common.items.armor.foot.cultist_boots", )), - back: None, + back: Some(assets::load_expect_cloned( + "common.items.armor.back.dungeon_purple_0", + )), ring: None, neck: None, lantern: Some(assets::load_expect_cloned("common.items.lantern.black_0")), diff --git a/voxygen/src/anim/src/character/alpha.rs b/voxygen/src/anim/src/character/alpha.rs index ecaca5f4c9..56823da136 100644 --- a/voxygen/src/anim/src/character/alpha.rs +++ b/voxygen/src/anim/src/character/alpha.rs @@ -359,7 +359,8 @@ impl Animation for AlphaAnimation { Some(ToolKind::Staff(_)) => { next.head.offset = Vec3::new( 0.0, - 0.0 + skeleton_attr.head.0 + decel * 0.8, + 0.0 + skeleton_attr.head.0, /* + decel * 0.8 */ + // Had some clipping issues skeleton_attr.head.1, ); next.head.ori = Quaternion::rotation_z(decel * 0.25) diff --git a/voxygen/src/anim/src/character/idle.rs b/voxygen/src/anim/src/character/idle.rs index 4a73cede6a..e7d612ca65 100644 --- a/voxygen/src/anim/src/character/idle.rs +++ b/voxygen/src/anim/src/character/idle.rs @@ -57,6 +57,9 @@ impl Animation for IdleAnimation { next.shorts.ori = Quaternion::rotation_x(0.0); next.shorts.scale = Vec3::one(); + next.back.offset = Vec3::new(0.0, skeleton_attr.back.0, skeleton_attr.back.1); + next.back.scale = Vec3::one() * 1.02; + next.l_hand.offset = Vec3::new( -skeleton_attr.hand.0, skeleton_attr.hand.1 + wave_ultra_slow_cos * 0.15, diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index a025369b75..2898937ae7 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -243,6 +243,7 @@ pub struct HudInfo { } pub enum Event { + ToggleTips(bool), SendMessage(String), AdjustMousePan(u32), AdjustMouseZoom(u32), @@ -1716,6 +1717,9 @@ impl Hud { }, settings_window::Event::ToggleHelp => self.show.help = !self.show.help, settings_window::Event::ToggleDebug => self.show.debug = !self.show.debug, + settings_window::Event::ToggleTips(loading_tips) => { + events.push(Event::ToggleTips(loading_tips)); + }, settings_window::Event::ChangeTab(tab) => self.show.open_setting_tab(tab), settings_window::Event::Close => { // Unpause the game if we are on singleplayer so that we can logout diff --git a/voxygen/src/hud/settings_window.rs b/voxygen/src/hud/settings_window.rs index 9c16b920be..9c92324eb3 100644 --- a/voxygen/src/hud/settings_window.rs +++ b/voxygen/src/hud/settings_window.rs @@ -51,10 +51,10 @@ widget_ids! { languages_list, rectangle, general_txt, + load_tips_button, + load_tips_button_label, debug_button, debug_button_label, - tips_button, - tips_button_label, interface, language_text, mouse_pan_slider, @@ -215,6 +215,7 @@ pub struct State { pub enum Event { ToggleHelp, ToggleDebug, + ToggleTips(bool), ToggleXpBar(XpBar), ToggleBarNumbers(BarNumbers), ToggleShortcutNumbers(ShortcutNumbers), @@ -399,6 +400,31 @@ impl<'a> Widget for SettingsWindow<'a> { .color(TEXT_COLOR) .set(state.ids.show_help_label, ui); + // Loading Screen Tips + let show_tips = ToggleButton::new( + self.global_state.settings.gameplay.loading_tips, + self.imgs.checkbox, + self.imgs.checkbox_checked, + ) + .w_h(18.0, 18.0) + .down_from(state.ids.button_help, 8.0) + .hover_images(self.imgs.checkbox_mo, self.imgs.checkbox_checked_mo) + .press_images(self.imgs.checkbox_press, self.imgs.checkbox_checked) + .set(state.ids.load_tips_button, ui); + + if self.global_state.settings.gameplay.loading_tips != show_tips { + events.push(Event::ToggleTips( + !self.global_state.settings.gameplay.loading_tips, + )); + } + + Text::new(&self.localized_strings.get("hud.settings.loading_tips")) + .right_from(state.ids.load_tips_button, 10.0) + .font_size(self.fonts.cyri.scale(14)) + .font_id(self.fonts.cyri.conrod_id) + .graphics_for(state.ids.load_tips_button) + .color(TEXT_COLOR) + .set(state.ids.load_tips_button_label, ui); // Debug let show_debug = ToggleButton::new( self.show.debug, @@ -406,7 +432,7 @@ impl<'a> Widget for SettingsWindow<'a> { self.imgs.checkbox_checked, ) .w_h(18.0, 18.0) - .down_from(state.ids.button_help, 8.0) + .down_from(state.ids.load_tips_button, 8.0) .hover_images(self.imgs.checkbox_mo, self.imgs.checkbox_checked_mo) .press_images(self.imgs.checkbox_press, self.imgs.checkbox_checked) .set(state.ids.debug_button, ui); diff --git a/voxygen/src/menu/main/ui.rs b/voxygen/src/menu/main/ui.rs index 9ba01eb8a0..e1f17b4440 100644 --- a/voxygen/src/menu/main/ui.rs +++ b/voxygen/src/menu/main/ui.rs @@ -17,7 +17,7 @@ use conrod_core::{ widget::{text_box::Event as TextBoxEvent, Button, Image, List, Rectangle, Text, TextBox}, widget_ids, Borderable, Color, Colorable, Labelable, Positionable, Sizeable, Widget, }; -use rand::{seq::SliceRandom, thread_rng}; +use rand::{seq::SliceRandom, thread_rng, Rng}; use std::time::Duration; const COL1: Color = Color::Rgba(0.07, 0.1, 0.1, 0.9); @@ -79,6 +79,9 @@ widget_ids! { info_bottom, // Auth Trust Prompt button_add_auth_trust, + // Loading Screen Tips + tip_txt_bg, + tip_txt, } } @@ -165,9 +168,10 @@ pub struct MainMenuUi { bg_img_id: conrod_core::image::Id, voxygen_i18n: std::sync::Arc, fonts: ConrodVoxygenFonts, + tip_no: u16, } -impl MainMenuUi { +impl<'a> MainMenuUi { pub fn new(global_state: &mut GlobalState) -> Self { let window = &mut global_state.window; let networking = &global_state.settings.networking; @@ -203,6 +207,7 @@ impl MainMenuUi { let bg_img_id = ui.add_graphic(Graphic::Image(load_expect( bg_imgs.choose(&mut rng).unwrap(), ))); + //let chosen_tip = *tips.choose(&mut rng).unwrap(); // Load language let voxygen_i18n = load_expect::(&i18n_asset_key( &global_state.settings.language.selected_language, @@ -233,6 +238,7 @@ impl MainMenuUi { bg_img_id, voxygen_i18n, fonts, + tip_no: 0, } } @@ -244,6 +250,13 @@ impl MainMenuUi { self.time = self.time + dt.as_secs_f32(); let fade_msg = (self.time * 2.0).sin() * 0.5 + 0.51; let (ref mut ui_widgets, ref mut _tooltip_manager) = self.ui.set_widgets(); + let tip_msg = format!( + "{} {}", + &self.voxygen_i18n.get("main.tip"), + &self.voxygen_i18n.get_variation("loading.tips", self.tip_no), + ); + let tip_show = global_state.settings.gameplay.loading_tips; + let mut rng = thread_rng(); let version = format!( "{}-{}", env!("CARGO_PKG_VERSION"), @@ -251,6 +264,7 @@ impl MainMenuUi { ); const TEXT_COLOR: Color = Color::Rgba(1.0, 1.0, 1.0, 1.0); const TEXT_COLOR_2: Color = Color::Rgba(1.0, 1.0, 1.0, 0.2); + const TEXT_BG: Color = Color::Rgba(0.0, 0.0, 0.0, 1.0); //const INACTIVE: Color = Color::Rgba(0.47, 0.47, 0.47, 0.47); let intro_text = &self.voxygen_i18n.get("main.login_process"); @@ -296,8 +310,22 @@ impl MainMenuUi { _ => self.imgs.f5, }) .w_h(74.0, 62.0) - .bottom_right_with_margins_on(self.ids.bg, 10.0, 10.0) + .bottom_left_with_margins_on(self.ids.bg, 10.0, 10.0) .set(self.ids.gears, ui_widgets); + if tip_show { + Text::new(&tip_msg) + .color(TEXT_BG) + .mid_bottom_with_margin_on(ui_widgets.window, 80.0) + .font_id(self.fonts.cyri.conrod_id) + .font_size(self.fonts.cyri.scale(20)) + .set(self.ids.tip_txt_bg, ui_widgets); + Text::new(&tip_msg) + .color(TEXT_COLOR) + .bottom_left_with_margins_on(self.ids.tip_txt_bg, 2.0, 2.0) + .font_id(self.fonts.cyri.conrod_id) + .font_size(self.fonts.cyri.scale(20)) + .set(self.ids.tip_txt, ui_widgets); + }; }; // Version displayed top right corner @@ -358,16 +386,16 @@ impl MainMenuUi { .middle_of(self.ids.login_error_bg) .set(self.ids.error_frame, ui_widgets); if let PopupType::ConnectionInfo = popup_type { - text.mid_top_with_margin_on(self.ids.error_frame, 10.0) - .font_id(self.fonts.alkhemi.conrod_id) - .bottom_left_with_margins_on(ui_widgets.window, 60.0, 60.0) - .font_size(self.fonts.cyri.scale(70)) - .set(self.ids.login_error, ui_widgets); + /*text.mid_top_with_margin_on(self.ids.error_frame, 10.0) + .font_id(self.fonts.cyri.conrod_id) + .bottom_left_with_margins_on(self.ids.bg, 30.0, 95.0) + .font_size(self.fonts.cyri.scale(35)) + .set(self.ids.login_error, ui_widgets);*/ } else { text.mid_top_with_margin_on(self.ids.error_frame, 10.0) .w(frame_w - 10.0 * 2.0) .font_id(self.fonts.cyri.conrod_id) - .font_size(self.fonts.cyri.scale(25)) + .font_size(self.fonts.cyri.scale(20)) .set(self.ids.login_error, ui_widgets); }; if Button::image(self.imgs.button) @@ -533,7 +561,7 @@ impl MainMenuUi { self.connect = true; self.connecting = Some(std::time::Instant::now()); self.popup = Some(PopupData { - msg: [self.voxygen_i18n.get("main.creating_world"), "..."].concat(), + msg: [self.voxygen_i18n.get(""), ""].concat(), popup_type: PopupType::ConnectionInfo, }); }; @@ -716,6 +744,7 @@ impl MainMenuUi { .set(self.ids.login_button, ui_widgets) .was_clicked() { + self.tip_no = rng.gen(); login!(); } @@ -737,6 +766,7 @@ impl MainMenuUi { .set(self.ids.singleplayer_button, ui_widgets) .was_clicked() { + self.tip_no = rng.gen(); singleplayer!(); } } diff --git a/voxygen/src/session.rs b/voxygen/src/session.rs index 44602088fc..e531fa04a2 100644 --- a/voxygen/src/session.rs +++ b/voxygen/src/session.rs @@ -746,6 +746,10 @@ impl PlayState for SessionState { global_state.settings.gameplay.sct_player_batch = sct_player_batch; global_state.settings.save_to_file_warn(); }, + HudEvent::ToggleTips(loading_tips) => { + global_state.settings.gameplay.loading_tips = loading_tips; + global_state.settings.save_to_file_warn(); + }, HudEvent::SctDamageBatch(sct_damage_batch) => { global_state.settings.gameplay.sct_damage_batch = sct_damage_batch; global_state.settings.save_to_file_warn(); diff --git a/voxygen/src/settings.rs b/voxygen/src/settings.rs index 3aec021d4c..1767ee8764 100644 --- a/voxygen/src/settings.rs +++ b/voxygen/src/settings.rs @@ -499,6 +499,7 @@ pub struct GameplaySettings { pub auto_walk_behavior: PressBehavior, pub stop_auto_walk_on_input: bool, pub map_zoom: f64, + pub loading_tips: bool, } impl Default for GameplaySettings { @@ -528,6 +529,7 @@ impl Default for GameplaySettings { auto_walk_behavior: PressBehavior::Toggle, stop_auto_walk_on_input: true, map_zoom: 4.0, + loading_tips: true, } } } diff --git a/world/src/site/dungeon/mod.rs b/world/src/site/dungeon/mod.rs index 8f3124d705..34d6a0aebf 100644 --- a/world/src/site/dungeon/mod.rs +++ b/world/src/site/dungeon/mod.rs @@ -533,6 +533,9 @@ impl Floor { 11 => comp::Item::expect_from_asset( "common.items.weapons.sword.cultist_purp_2h-0", ), + 12 => comp::Item::expect_from_asset( + "common.items.armor.back.dungeon_purple-0", + ), _ => comp::Item::expect_from_asset( "common.items.boss_drops.exp_flask", ), From 9497d7e711981567296177f796b7fa21ba976ae7 Mon Sep 17 00:00:00 2001 From: Monty Marz Date: Thu, 23 Jul 2020 15:01:39 +0200 Subject: [PATCH 78/85] Update terrain.rs --- server/src/sys/terrain.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/sys/terrain.rs b/server/src/sys/terrain.rs index ed5acff8df..e28727675d 100644 --- a/server/src/sys/terrain.rs +++ b/server/src/sys/terrain.rs @@ -209,7 +209,7 @@ impl<'a> System<'a> for Sys { "common.items.armor.foot.cultist_boots", )), back: Some(assets::load_expect_cloned( - "common.items.armor.back.dungeon_purple_0", + "common.items.armor.back.dungeon_purple-0", )), ring: None, neck: None, From f7a3a622e59beeacf66b6424188e3b4f520a875b Mon Sep 17 00:00:00 2001 From: lausek Date: Thu, 23 Jul 2020 17:16:52 +0000 Subject: [PATCH 79/85] fix #660; Admins should join even if they aren't on whitelist --- server/src/events/player.rs | 8 +-- server/src/lib.rs | 11 ++- .../{auth_provider.rs => login_provider.rs} | 68 +++++++++++-------- server/src/sys/message.rs | 21 +++--- 4 files changed, 61 insertions(+), 47 deletions(-) rename server/src/{auth_provider.rs => login_provider.rs} (54%) diff --git a/server/src/events/player.rs b/server/src/events/player.rs index b44f61d7f4..0e7bd7120d 100644 --- a/server/src/events/player.rs +++ b/server/src/events/player.rs @@ -1,6 +1,6 @@ use super::Event; use crate::{ - auth_provider::AuthProvider, client::Client, persistence, state_ext::StateExt, Server, + client::Client, login_provider::LoginProvider, persistence, state_ext::StateExt, Server, }; use common::{ comp, @@ -68,11 +68,11 @@ pub fn handle_client_disconnect(server: &mut Server, entity: EcsEntity) -> Event state.notify_registered_clients(ServerMsg::PlayerListUpdate(PlayerListUpdate::Remove(*uid))) } - // Make sure to remove the player from the logged in list. (See AuthProvider) + // Make sure to remove the player from the logged in list. (See LoginProvider) // And send a disconnected message if let Some(player) = state.ecs().read_storage::().get(entity) { - let mut accounts = state.ecs().write_resource::(); - accounts.logout(player.uuid()); + let mut login_provider = state.ecs().write_resource::(); + login_provider.logout(player.uuid()); let msg = comp::ChatType::Offline.server_msg(format!("[{}] went offline.", &player.alias)); state.notify_registered_clients(msg); diff --git a/server/src/lib.rs b/server/src/lib.rs index c37fdb9708..2f5fff5ae9 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -3,13 +3,13 @@ #![feature(drain_filter, option_zip)] pub mod alias_validator; -pub mod auth_provider; pub mod chunk_generator; pub mod client; pub mod cmd; pub mod error; pub mod events; pub mod input; +pub mod login_provider; pub mod metrics; pub mod persistence; pub mod settings; @@ -22,10 +22,10 @@ pub use crate::{error::Error, events::Event, input::Input, settings::ServerSetti use crate::{ alias_validator::AliasValidator, - auth_provider::AuthProvider, chunk_generator::ChunkGenerator, client::{Client, RegionSubscription}, cmd::ChatCommandExt, + login_provider::LoginProvider, state_ext::StateExt, sys::sentinel::{DeletedEntities, TrackedComps}, }; @@ -99,10 +99,9 @@ impl Server { let mut state = State::default(); state.ecs_mut().insert(settings.clone()); state.ecs_mut().insert(EventBus::::default()); - state.ecs_mut().insert(AuthProvider::new( - settings.auth_server_address.clone(), - settings.whitelist.clone(), - )); + state + .ecs_mut() + .insert(LoginProvider::new(settings.auth_server_address.clone())); state.ecs_mut().insert(Tick(0)); state.ecs_mut().insert(ChunkGenerator::new()); state diff --git a/server/src/auth_provider.rs b/server/src/login_provider.rs similarity index 54% rename from server/src/auth_provider.rs rename to server/src/login_provider.rs index 9b2c944f61..0410f528c7 100644 --- a/server/src/auth_provider.rs +++ b/server/src/login_provider.rs @@ -15,33 +15,64 @@ fn derive_uuid(username: &str) -> Uuid { Uuid::from_slice(&state.to_be_bytes()).unwrap() } -pub struct AuthProvider { +pub struct LoginProvider { accounts: HashMap, auth_server: Option, - whitelist: Vec, } -impl AuthProvider { - pub fn new(auth_addr: Option, whitelist: Vec) -> Self { +impl LoginProvider { + pub fn new(auth_addr: Option) -> Self { let auth_server = match auth_addr { Some(addr) => Some(AuthClient::new(addr)), None => None, }; - AuthProvider { + Self { accounts: HashMap::new(), auth_server, - whitelist, } } + fn login(&mut self, uuid: Uuid, username: String) -> Result<(), RegisterError> { + // make sure that the user is not logged in already + if self.accounts.contains_key(&uuid) { + return Err(RegisterError::AlreadyLoggedIn); + } + info!(?username, "New User"); + self.accounts.insert(uuid, username); + Ok(()) + } + pub fn logout(&mut self, uuid: Uuid) { if self.accounts.remove(&uuid).is_none() { error!(?uuid, "Attempted to logout user that is not logged in."); }; } - pub fn query(&mut self, username_or_token: String) -> Result<(String, Uuid), RegisterError> { + pub fn try_login( + &mut self, + username_or_token: &str, + whitelist: &[String], + ) -> Result<(String, Uuid), RegisterError> { + self + // resolve user information + .query(username_or_token) + // if found, check name against whitelist or if user is admin + .and_then(|(username, uuid)| { + // user can only join if he is admin, the whitelist is empty (everyone can join) + // or his name is in the whitelist + if !whitelist.is_empty() && !whitelist.contains(&username) { + return Err(RegisterError::NotOnWhitelist); + } + + // add the user to self.accounts + self.login(uuid, username.clone())?; + + Ok((username, uuid)) + }) + } + + pub fn query(&mut self, username_or_token: &str) -> Result<(String, Uuid), RegisterError> { // Based on whether auth server is provided or not we expect an username or // token match &self.auth_server { @@ -49,36 +80,19 @@ impl AuthProvider { Some(srv) => { info!(?username_or_token, "Validating token"); // Parse token - let token = AuthToken::from_str(&username_or_token) + let token = AuthToken::from_str(username_or_token) .map_err(|e| RegisterError::AuthError(e.to_string()))?; // Validate token let uuid = srv.validate(token)?; - // Check if already logged in - if self.accounts.contains_key(&uuid) { - return Err(RegisterError::AlreadyLoggedIn); - } let username = srv.uuid_to_username(uuid)?; - // Check if player is in whitelist - if self.whitelist.len() > 0 && !self.whitelist.contains(&username) { - return Err(RegisterError::NotOnWhitelist); - } - - // Log in - self.accounts.insert(uuid, username.clone()); Ok((username, uuid)) }, // Username is expected None => { // Assume username was provided let username = username_or_token; - let uuid = derive_uuid(&username); - if !self.accounts.contains_key(&uuid) { - info!(?username, "New User"); - self.accounts.insert(uuid, username.clone()); - Ok((username, uuid)) - } else { - Err(RegisterError::AlreadyLoggedIn) - } + let uuid = derive_uuid(username); + Ok((username.to_string(), uuid)) }, } } diff --git a/server/src/sys/message.rs b/server/src/sys/message.rs index 625b57c7d8..d2d7c895d6 100644 --- a/server/src/sys/message.rs +++ b/server/src/sys/message.rs @@ -1,6 +1,6 @@ use super::SysTimer; use crate::{ - alias_validator::AliasValidator, auth_provider::AuthProvider, client::Client, + alias_validator::AliasValidator, client::Client, login_provider::LoginProvider, persistence::character::CharacterLoader, ServerSettings, CLIENT_TIMEOUT, }; use common::{ @@ -45,7 +45,7 @@ impl Sys { force_updates: &ReadStorage<'_, ForceUpdate>, stats: &mut WriteStorage<'_, Stats>, chat_modes: &ReadStorage<'_, ChatMode>, - accounts: &mut WriteExpect<'_, AuthProvider>, + login_provider: &mut WriteExpect<'_, LoginProvider>, block_changes: &mut Write<'_, BlockChange>, admin_list: &ReadExpect<'_, AdminList>, admins: &mut WriteStorage<'_, Admin>, @@ -86,13 +86,14 @@ impl Sys { view_distance, token_or_username, } => { - let (username, uuid) = match accounts.query(token_or_username.clone()) { - Err(err) => { - client.error_state(RequestStateError::RegisterDenied(err)); - break Ok(()); - }, - Ok((username, uuid)) => (username, uuid), - }; + let (username, uuid) = + match login_provider.try_login(&token_or_username, &settings.whitelist) { + Err(err) => { + client.error_state(RequestStateError::RegisterDenied(err)); + break Ok(()); + }, + Ok((username, uuid)) => (username, uuid), + }; let vd = view_distance.map(|vd| vd.min(settings.max_view_distance.unwrap_or(vd))); @@ -411,7 +412,7 @@ impl<'a> System<'a> for Sys { ReadStorage<'a, ForceUpdate>, WriteStorage<'a, Stats>, ReadStorage<'a, ChatMode>, - WriteExpect<'a, AuthProvider>, + WriteExpect<'a, LoginProvider>, Write<'a, BlockChange>, ReadExpect<'a, AdminList>, WriteStorage<'a, Admin>, From 20c2fd2c0bf5c60706fd054af0b1044118a499aa Mon Sep 17 00:00:00 2001 From: Snowram Date: Thu, 23 Jul 2020 20:31:47 +0200 Subject: [PATCH 80/85] Loading tips and villagers attack speech --- assets/voxygen/i18n/fr_FR.ron | 116 +++++++++++++++++++++++++++++++++- 1 file changed, 113 insertions(+), 3 deletions(-) diff --git a/assets/voxygen/i18n/fr_FR.ron b/assets/voxygen/i18n/fr_FR.ron index 9facaf3492..478b6cf011 100644 --- a/assets/voxygen/i18n/fr_FR.ron +++ b/assets/voxygen/i18n/fr_FR.ron @@ -115,6 +115,7 @@ https://account.veloren.net."#, "main.login.invalid_character": "Le personnage sélectionné n'est pas valide", "main.login.client_crashed": "Le client a planté", "main.login.not_on_whitelist": "Vous devez être ajouté à la liste blanche par un Admin pour pouvoir entrer", + "main.tip": "Astuce:", /// End Main screen section @@ -251,6 +252,7 @@ objets magiques ?"#, "hud.settings.chat": "Tchat", "hud.settings.background_transparency": "Transparence du fond", "hud.settings.chat_character_name": "Nom des personnages dans le tchat", + "hud.settings.loading_tips": "Astuces de chargement", "hud.settings.pan_sensitivity": "Sensibilité de la souris", "hud.settings.zoom_sensitivity": "Sensibilité du zoom", @@ -394,9 +396,117 @@ Force Dexterité -Intelligence"#, +Protection +"#, + /// End character window section + + + /// Start Escape Menu Section + "esc_menu.logout": "Déconnexion", + "esc_menu.quit_game": "Quitter le jeu", + /// End Escape Menu Section + }, - vector_map: { + vector_map: { + "loading.tips": [ + "Appuie sur 'G' pour allumer ta lanterne.", + "Appuie sur 'F1' pour voir les raccourcis clavier par défaut.", + "Tu peux écrire /say ou /s pour discuter aux joueurs directement à côté toi.", + "Tu peux écrire /region ou /r pour discuter avec les joueurs situés à quelques centaines de blocs de toi.", + "Pour envoyer un message privé, écrit /tell suivi par un nom de joueur puis ton message.", + "Des PNJs avec le même niveau peuvent varier en difficulté.", + "Regarde le sol pour trouver de la nourriture, des coffres et d'autres butins!", + "Ton inventaire est rempli de nourriture? Essaie de créer un meilleur repas avec!", + "Tu cherches une activité? Les donjons sont marqués avec des points marron sur la carte!", + "N'oublie pas d'ajuster les graphiques pour ton système. Appuie sur 'N' pour ouvrir les paramètres.", + "Jouer à plusieurs est amusant! Appuie sur 'O' pour voir qui est en ligne.", + "Un PNJ avec une tête-de-mort sous sa barre de vie est plus puissant que toi.", + "Appuie sur 'J' pour danser. C'est la fête!", + "Appuie sur 'L-Shift'pour ouvrir ton deltaplane et conquérir les cieux.", + "Veloren est encore Pre-Alpha. Nous faisons de notre mieux pour l'améliorer chaque jour!", + "Si tu veux te joindre à l'équipe de développement ou juste discuter avec nous, rejoins notre serveur Discord.", + ], + "npc.speech.villager_under_attack": [ + "À l'aide, on m'attaque!", + "À l'aide! On m'attaque!", + "Aïe! On m'attaque!", + "Aïe! On m'attaque! À l'aide!", + "Aidez-moi! On m'attaque!", + "On m'attaque! À l'aide!", + "On m'attaque! Aidez-moi!", + "À l'aide!", + "À l'aide! À l'aide!", + "À l'aide! À l'aide! À l'aide!", + "On m'attaque!", + "AAAHHH! On m'attaque!", + "AAAHHH! On m'attaque! À l'aide!", + "À l'aide! Nous sommes attaqués!", + "À l'aide! Assassin!", + "À l'aide! Il y a un assassin en liberté!", + "À l'aide! On essaie de me tuer!", + "Gardes, on m'attaque!", + "Gardes! On m'attaque!", + "On m'attaque! Gardes!", + "À l'aide! Gardes! On m'attaque!", + "Gardes! Venez vite!", + "Gardes! Gardes!", + "Gardes! Un scélérat m'attaque!", + "Gardes, abattez ce scélérat!", + "Gardes! Il y a un meurtrier!", + "Gardes! Aidez-moi!", + "Vous ne vous en tirerez pas comme ça! Gardes!", + "Monstre!", + "Aidez-moi!", + "À l'aide! S'il vous plait!", + "Aïe! Gardes! À l'aide!", + "Ils viennent pour moi !", + "À l'aide! À l'aide! Je me fais réprimer!", + "Ah, nous voyons maintenant la violence inhérente au système.", + "C'est seulement une égratignure.", + "Arrêtez ça!", + "Qu'est ce que je t'ai fait?!", + "S'il te plaît arrête de m'attaquer!", + "Hé! Regardez où vous pointez cette chose!", + "Misérable, allez-vous-en!", + "Arrêtez! Partez! Arrêtez!", + "Vous m'avez ennervé!", + "Oi! Qui croyez-vous être?!", + "J'aurais votre tête pour ça!", + "Stoppez s'il vous plaît! Je ne transporte rien de valeur!", + "Je vais appeler mon frère, il est plus grand que moi!", + "Nooon, Je vais le dire à ma mère!", + "Soyez maudit!", + "Ne faites pas ça.", + "Ce n'était pas très gentil!", + "Ton arme fonctionne, tu peux la ranger maintenant!", + "Épargnez-moi!", + "Pitié, J'ai une famille!", + "Je suis trop jeune pour mourrir!", + "On peut en parler?", + "La violence n'est jamais la solution!", + "Aujourd'hui est une très mauvaise journée...", + "Hé, ça fait mal!", + "Aïe!", + "Quelle impolitesse!", + "Stop, je vous en prie!", + "Que la peste vous emporte!", + "Ce n'est pas amusant.", + "Comment osez-vous?!", + "Vous allez payer!", + "Continue et tu vas le regretter!", + "Ne m'obligez pas à vous faire du mal!", + "Il doit y avoir erreur!", + "Vous n'avez pas besoin de faire ça!", + "Fuyez, monstre!", + "Ça fait vraiment mal!", + "Pourquoi faites-vous cela?", + "Par les esprits, cessez!", + "Vous devez m'avoir confondu avec quelqu'un d'autre!", + "Je ne mérite pas cela!", + "Ne faites plus cela.", + "Gardes, jetez ce monstre dans le lac!", + "Je vais t'envoyer ma tarrasque!", + ], } -) +) \ No newline at end of file From 6435464d0064bf2137aad8a9bb9bda61da2fcfd4 Mon Sep 17 00:00:00 2001 From: Imbris Date: Sat, 25 Jul 2020 17:00:33 -0400 Subject: [PATCH 81/85] Make video mode selection slightly better --- voxygen/src/window.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/voxygen/src/window.rs b/voxygen/src/window.rs index 68a536a8ab..c154845a2b 100644 --- a/voxygen/src/window.rs +++ b/voxygen/src/window.rs @@ -1054,10 +1054,13 @@ impl Window { window .current_monitor() .video_modes() - .filter(|mode| mode.bit_depth() >= 24) + .filter(|mode| mode.bit_depth() >= 24 && mode.refresh_rate() >= 59) .max_by_key(|mode| mode.size().width) .unwrap_or_else(|| { - warn!("No video mode with a bit depth of at least 24 found"); + warn!( + "No video mode with a bit depth of at least 24 and a refresh rate of \ + at least 60Hz found" + ); window .current_monitor() .video_modes() From 527e7a329e2e19c63f51f11b12299efe620106a9 Mon Sep 17 00:00:00 2001 From: Samuel Keiffer Date: Sat, 25 Jul 2020 23:57:04 +0000 Subject: [PATCH 82/85] Armor fixes --- .../items/armor/chest/cultist_chest_blue.ron | 2 +- .../armor/chest/cultist_chest_purple.ron | 2 +- .../common/items/armor/foot/cultist_boots.ron | 2 +- .../items/armor/hand/cultist_hands_blue.ron | 2 +- .../items/armor/hand/cultist_hands_purple.ron | 2 +- .../items/armor/pants/cultist_legs_blue.ron | 2 +- .../items/armor/pants/cultist_legs_purple.ron | 2 +- .../armor/shoulder/cultist_shoulder_blue.ron | 2 +- .../shoulder/cultist_shoulder_purple.ron | 2 +- common/src/comp/damage.rs | 77 +++++++++++++++++++ common/src/comp/inventory/item/tool.rs | 21 +---- common/src/comp/mod.rs | 2 + common/src/comp/projectile.rs | 19 +---- common/src/states/basic_ranged.rs | 2 +- common/src/sys/combat.rs | 42 +++++----- common/src/sys/projectile.rs | 27 ++++++- server/src/events/entity_manipulation.rs | 42 ++++++---- .../down.sql | 1 + .../up.sql | 15 ++++ 19 files changed, 181 insertions(+), 85 deletions(-) create mode 100644 common/src/comp/damage.rs create mode 100644 server/src/migrations/2020-07-24-191205_fix_projectile_stats/down.sql create mode 100644 server/src/migrations/2020-07-24-191205_fix_projectile_stats/up.sql diff --git a/assets/common/items/armor/chest/cultist_chest_blue.ron b/assets/common/items/armor/chest/cultist_chest_blue.ron index ddb8dcac4c..8b1a34da39 100644 --- a/assets/common/items/armor/chest/cultist_chest_blue.ron +++ b/assets/common/items/armor/chest/cultist_chest_blue.ron @@ -5,7 +5,7 @@ Item( ( kind: Chest(CultistBlue), stats: ( - protection: Normal(5.0), + protection: Normal(30.0), ), ) ), diff --git a/assets/common/items/armor/chest/cultist_chest_purple.ron b/assets/common/items/armor/chest/cultist_chest_purple.ron index d043e49512..e35a727d6b 100644 --- a/assets/common/items/armor/chest/cultist_chest_purple.ron +++ b/assets/common/items/armor/chest/cultist_chest_purple.ron @@ -5,7 +5,7 @@ Item( ( kind: Chest(CultistPurple), stats: ( - protection: Normal(5.0), + protection: Normal(30.0), ), ) ), diff --git a/assets/common/items/armor/foot/cultist_boots.ron b/assets/common/items/armor/foot/cultist_boots.ron index d24453912e..2f9453db96 100644 --- a/assets/common/items/armor/foot/cultist_boots.ron +++ b/assets/common/items/armor/foot/cultist_boots.ron @@ -5,7 +5,7 @@ Item( ( kind: Foot(Cultist), stats: ( - protection: Normal(1.0), + protection: Normal(6.0), ), ) ), diff --git a/assets/common/items/armor/hand/cultist_hands_blue.ron b/assets/common/items/armor/hand/cultist_hands_blue.ron index 32875de0cd..c4c88cd131 100644 --- a/assets/common/items/armor/hand/cultist_hands_blue.ron +++ b/assets/common/items/armor/hand/cultist_hands_blue.ron @@ -5,7 +5,7 @@ Item( ( kind: Hand(CultistBlue), stats: ( - protection: Normal(2.0), + protection: Normal(12.0), ), ) ), diff --git a/assets/common/items/armor/hand/cultist_hands_purple.ron b/assets/common/items/armor/hand/cultist_hands_purple.ron index 3df26460d4..46e1f672d7 100644 --- a/assets/common/items/armor/hand/cultist_hands_purple.ron +++ b/assets/common/items/armor/hand/cultist_hands_purple.ron @@ -5,7 +5,7 @@ Item( ( kind: Hand(CultistPurple), stats: ( - protection: Normal(2.0), + protection: Normal(12.0), ), ) ), diff --git a/assets/common/items/armor/pants/cultist_legs_blue.ron b/assets/common/items/armor/pants/cultist_legs_blue.ron index 33fe4c2826..aabf7b54a6 100644 --- a/assets/common/items/armor/pants/cultist_legs_blue.ron +++ b/assets/common/items/armor/pants/cultist_legs_blue.ron @@ -5,7 +5,7 @@ Item( ( kind: Pants(CultistBlue), stats: ( - protection: Normal(3.0), + protection: Normal(24.0), ), ) ), diff --git a/assets/common/items/armor/pants/cultist_legs_purple.ron b/assets/common/items/armor/pants/cultist_legs_purple.ron index a6e7cdb7a5..a2609e55c9 100644 --- a/assets/common/items/armor/pants/cultist_legs_purple.ron +++ b/assets/common/items/armor/pants/cultist_legs_purple.ron @@ -5,7 +5,7 @@ Item( ( kind: Pants(CultistPurple), stats: ( - protection: Normal(3.0), + protection: Normal(24.0), ), ) ), diff --git a/assets/common/items/armor/shoulder/cultist_shoulder_blue.ron b/assets/common/items/armor/shoulder/cultist_shoulder_blue.ron index a7df2169aa..aa1125a7ac 100644 --- a/assets/common/items/armor/shoulder/cultist_shoulder_blue.ron +++ b/assets/common/items/armor/shoulder/cultist_shoulder_blue.ron @@ -5,7 +5,7 @@ Item( ( kind: Shoulder(CultistBlue), stats: ( - protection: Normal(3.0), + protection: Normal(18.0), ), ) ), diff --git a/assets/common/items/armor/shoulder/cultist_shoulder_purple.ron b/assets/common/items/armor/shoulder/cultist_shoulder_purple.ron index 3d01973bff..52953b9bdb 100644 --- a/assets/common/items/armor/shoulder/cultist_shoulder_purple.ron +++ b/assets/common/items/armor/shoulder/cultist_shoulder_purple.ron @@ -5,7 +5,7 @@ Item( ( kind: Shoulder(CultistPurple), stats: ( - protection: Normal(3.0), + protection: Normal(18.0), ), ) ), diff --git a/common/src/comp/damage.rs b/common/src/comp/damage.rs new file mode 100644 index 0000000000..e2e6449975 --- /dev/null +++ b/common/src/comp/damage.rs @@ -0,0 +1,77 @@ +use crate::comp::Loadout; +use serde::{Deserialize, Serialize}; + +pub const BLOCK_EFFICIENCY: f32 = 0.9; + +pub struct Damage { + pub healthchange: f32, + pub source: DamageSource, +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub enum DamageSource { + Melee, + Healing, + Projectile, + Explosion, + Falling, +} + +impl Damage { + pub fn modify_damage(&mut self, block: bool, loadout: &Loadout) { + match self.source { + DamageSource::Melee => { + // Critical hit + if rand::random() { + self.healthchange *= 1.2; + } + // Block + if block { + self.healthchange *= 1.0 - BLOCK_EFFICIENCY + } + // Armor + self.healthchange *= 1.0 - loadout.get_damage_reduction(); + + // Min damage + if self.healthchange > -1.0 { + self.healthchange = -1.0; + } + }, + DamageSource::Projectile => { + // Critical hit + if rand::random() { + self.healthchange *= 1.2; + } + // Block + if block { + self.healthchange *= 1.0 - BLOCK_EFFICIENCY + } + // Armor + self.healthchange *= 1.0 - loadout.get_damage_reduction(); + + // Min damage + if self.healthchange > -1.0 { + self.healthchange = -1.0; + } + }, + DamageSource::Explosion => { + // Critical hit + if rand::random() { + self.healthchange *= 1.2; + } + // Block + if block { + self.healthchange *= 1.0 - BLOCK_EFFICIENCY + } + // Armor + self.healthchange *= 1.0 - loadout.get_damage_reduction(); + + // Min damage + if self.healthchange > -1.0 { + self.healthchange = -1.0; + } + }, + _ => {}, + } + } +} diff --git a/common/src/comp/inventory/item/tool.rs b/common/src/comp/inventory/item/tool.rs index 4409551306..3c92e7c32b 100644 --- a/common/src/comp/inventory/item/tool.rs +++ b/common/src/comp/inventory/item/tool.rs @@ -2,8 +2,7 @@ // version in voxygen\src\meta.rs in order to reset save files to being empty use crate::comp::{ - body::object, projectile, Body, CharacterAbility, Gravity, HealthChange, HealthSource, - LightEmitter, Projectile, + body::object, projectile, Body, CharacterAbility, Gravity, LightEmitter, Projectile, }; use serde::{Deserialize, Serialize}; use std::time::Duration; @@ -262,11 +261,7 @@ impl Tool { projectile: Projectile { hit_solid: vec![projectile::Effect::Stick], hit_entity: vec![ - projectile::Effect::Damage(HealthChange { - // TODO: This should not be fixed (?) - amount: -3, - cause: HealthSource::Projectile { owner: None }, - }), + projectile::Effect::Damage(-3), projectile::Effect::Knockback(10.0), projectile::Effect::RewardEnergy(100), projectile::Effect::Vanish, @@ -286,11 +281,7 @@ impl Tool { projectile: Projectile { hit_solid: vec![projectile::Effect::Stick], hit_entity: vec![ - projectile::Effect::Damage(HealthChange { - // TODO: This should not be fixed (?) - amount: -9, - cause: HealthSource::Projectile { owner: None }, - }), + projectile::Effect::Damage(-9), projectile::Effect::Knockback(15.0), projectile::Effect::RewardEnergy(50), projectile::Effect::Vanish, @@ -336,11 +327,7 @@ impl Tool { projectile: Projectile { hit_solid: vec![projectile::Effect::Vanish], hit_entity: vec![ - projectile::Effect::Damage(HealthChange { - // TODO: This should not be fixed (?) - amount: -3, - cause: HealthSource::Projectile { owner: None }, - }), + projectile::Effect::Damage(-3), projectile::Effect::RewardEnergy(150), projectile::Effect::Vanish, ], diff --git a/common/src/comp/mod.rs b/common/src/comp/mod.rs index 8ab32575f6..b99b575450 100644 --- a/common/src/comp/mod.rs +++ b/common/src/comp/mod.rs @@ -5,6 +5,7 @@ mod body; mod character_state; mod chat; mod controller; +mod damage; mod energy; mod inputs; mod inventory; @@ -32,6 +33,7 @@ pub use controller::{ Climb, ControlAction, ControlEvent, Controller, ControllerInputs, Input, InventoryManip, MountState, Mounting, }; +pub use damage::{Damage, DamageSource}; pub use energy::{Energy, EnergySource}; pub use inputs::CanBuild; pub use inventory::{ diff --git a/common/src/comp/projectile.rs b/common/src/comp/projectile.rs index d029075850..8346b7cf52 100644 --- a/common/src/comp/projectile.rs +++ b/common/src/comp/projectile.rs @@ -1,4 +1,4 @@ -use crate::{comp, sync::Uid}; +use crate::sync::Uid; use serde::{Deserialize, Serialize}; use specs::{Component, FlaggedStorage}; use specs_idvs::IdvStorage; @@ -6,7 +6,7 @@ use std::time::Duration; #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub enum Effect { - Damage(comp::HealthChange), + Damage(i32), Knockback(f32), RewardEnergy(u32), Explode { power: f32 }, @@ -25,21 +25,6 @@ pub struct Projectile { pub owner: Option, } -impl Projectile { - pub fn set_owner(&mut self, new_owner: Uid) { - self.owner = Some(new_owner); - for e in self.hit_solid.iter_mut().chain(self.hit_entity.iter_mut()) { - if let Effect::Damage(comp::HealthChange { - cause: comp::HealthSource::Projectile { owner, .. }, - .. - }) = e - { - *owner = Some(new_owner); - } - } - } -} - impl Component for Projectile { type Storage = FlaggedStorage>; } diff --git a/common/src/states/basic_ranged.rs b/common/src/states/basic_ranged.rs index 5b1f7cf0d7..b41bd6c208 100644 --- a/common/src/states/basic_ranged.rs +++ b/common/src/states/basic_ranged.rs @@ -54,7 +54,7 @@ impl CharacterBehavior for Data { } else if !self.exhausted { // Fire let mut projectile = self.projectile.clone(); - projectile.set_owner(*data.uid); + projectile.owner = Some(*data.uid); update.server_events.push_front(ServerEvent::Shoot { entity: data.entity, dir: data.inputs.look_dir, diff --git a/common/src/sys/combat.rs b/common/src/sys/combat.rs index f11714b23b..d585171b0d 100644 --- a/common/src/sys/combat.rs +++ b/common/src/sys/combat.rs @@ -1,7 +1,7 @@ use crate::{ comp::{ - Alignment, Attacking, Body, CharacterState, HealthChange, HealthSource, Loadout, Ori, Pos, - Scale, Stats, + Alignment, Attacking, Body, CharacterState, Damage, DamageSource, HealthChange, + HealthSource, Loadout, Ori, Pos, Scale, Stats, }, event::{EventBus, LocalEvent, ServerEvent}, sync::Uid, @@ -112,7 +112,15 @@ impl<'a> System<'a> for Sys { && ori2.angle_between(pos_b2 - pos2) < attack.max_angle + (rad_b / pos2.distance(pos_b2)).atan() { // Weapon gives base damage - let mut healthchange = attack.base_healthchange as f32; + let source = if attack.base_healthchange > 0 { + DamageSource::Healing + } else { + DamageSource::Melee + }; + let mut damage = Damage { + healthchange: attack.base_healthchange as f32, + source, + }; let mut knockback = attack.knockback; // TODO: remove this, either it will remain unused or be used as a temporary @@ -124,40 +132,30 @@ impl<'a> System<'a> for Sys { // TODO: remove this when there is a better way to deal with alignment // Don't heal NPCs - if (healthchange > 0.0 && alignment_b_maybe + if (damage.healthchange > 0.0 && alignment_b_maybe .map(|a| !a.is_friendly_to_players()) .unwrap_or(true)) // Don't hurt pets - || (healthchange < 0.0 && alignment_b_maybe + || (damage.healthchange < 0.0 && alignment_b_maybe .map(|b| Alignment::Owned(*uid).passive_towards(*b)) .unwrap_or(false)) { - healthchange = 0.0; + damage.healthchange = 0.0; knockback = 0.0; } - if rand::random() { - healthchange *= 1.2; - } + let block = character_b.map(|c_b| c_b.is_block()).unwrap_or(false) + && ori_b.0.angle_between(pos.0 - pos_b.0) < BLOCK_ANGLE.to_radians() / 2.0; - // Block - if character_b.map(|c_b| c_b.is_block()).unwrap_or(false) - && ori_b.0.angle_between(pos.0 - pos_b.0) < BLOCK_ANGLE.to_radians() / 2.0 - { - healthchange *= 1.0 - BLOCK_EFFICIENCY - } - - // Armor if let Some(loadout) = loadouts.get(b) { - let damage_reduction = loadout.get_damage_reduction(); - healthchange *= 1.0 - damage_reduction; + damage.modify_damage(block, loadout); } - if healthchange != 0.0 { + if damage.healthchange != 0.0 { server_emitter.emit(ServerEvent::Damage { uid: *uid_b, change: HealthChange { - amount: healthchange as i32, + amount: damage.healthchange as i32, cause: HealthSource::Attack { by: *uid }, }, }); @@ -165,7 +163,7 @@ impl<'a> System<'a> for Sys { if knockback != 0.0 { local_emitter.emit(LocalEvent::ApplyForce { entity: b, - force: attack.knockback + force: knockback * *Dir::slerp(ori.0, Dir::new(Vec3::new(0.0, 0.0, 1.0)), 0.5), }); } diff --git a/common/src/sys/projectile.rs b/common/src/sys/projectile.rs index 306b0bab10..ad2e4a1023 100644 --- a/common/src/sys/projectile.rs +++ b/common/src/sys/projectile.rs @@ -1,7 +1,7 @@ use crate::{ comp::{ - projectile, Alignment, Energy, EnergySource, HealthSource, Ori, PhysicsState, Pos, - Projectile, Vel, + projectile, Alignment, Damage, DamageSource, Energy, EnergySource, HealthChange, + HealthSource, Loadout, Ori, PhysicsState, Pos, Projectile, Vel, }, event::{EventBus, LocalEvent, ServerEvent}, state::DeltaTime, @@ -29,6 +29,7 @@ impl<'a> System<'a> for Sys { WriteStorage<'a, Projectile>, WriteStorage<'a, Energy>, ReadStorage<'a, Alignment>, + ReadStorage<'a, Loadout>, ); fn run( @@ -46,6 +47,7 @@ impl<'a> System<'a> for Sys { mut projectiles, mut energies, alignments, + loadouts, ): Self::SystemData, ) { let mut local_emitter = local_bus.emitter(); @@ -84,8 +86,19 @@ impl<'a> System<'a> for Sys { else if let Some(other) = physics.touch_entity { for effect in projectile.hit_entity.drain(..) { match effect { - projectile::Effect::Damage(change) => { + projectile::Effect::Damage(healthchange) => { let owner_uid = projectile.owner.unwrap(); + let mut damage = Damage { + healthchange: healthchange as f32, + source: DamageSource::Projectile, + }; + if let Some(entity) = + uid_allocator.retrieve_entity_internal(other.into()) + { + if let Some(loadout) = loadouts.get(entity) { + damage.modify_damage(false, loadout); + } + } // Hacky: remove this when groups get implemented let passive = uid_allocator .retrieve_entity_internal(other.into()) @@ -96,7 +109,13 @@ impl<'a> System<'a> for Sys { }) .unwrap_or(false); if other != projectile.owner.unwrap() && !passive { - server_emitter.emit(ServerEvent::Damage { uid: other, change }); + server_emitter.emit(ServerEvent::Damage { + uid: other, + change: HealthChange { + amount: damage.healthchange as i32, + cause: HealthSource::Attack { by: owner_uid }, + }, + }); } }, projectile::Effect::Knockback(knockback) => { diff --git a/server/src/events/entity_manipulation.rs b/server/src/events/entity_manipulation.rs index 454f8f95e9..521acfbed1 100644 --- a/server/src/events/entity_manipulation.rs +++ b/server/src/events/entity_manipulation.rs @@ -1,11 +1,14 @@ use crate::{client::Client, Server, SpawnPoint, StateExt}; use common::{ assets, - comp::{self, item::lottery::Lottery, object, Body, HealthChange, HealthSource, Player, Stats}, + comp::{ + self, item::lottery::Lottery, object, Body, Damage, DamageSource, HealthChange, + HealthSource, Player, Stats, + }, msg::{PlayerListUpdate, ServerMsg}, state::BlockChange, sync::{Uid, WorldSyncExt}, - sys::combat::{BLOCK_ANGLE, BLOCK_EFFICIENCY}, + sys::combat::BLOCK_ANGLE, terrain::{Block, TerrainGrid}, vol::{ReadVol, Vox}, }; @@ -159,9 +162,16 @@ pub fn handle_land_on_ground(server: &Server, entity: EcsEntity, vel: Vec3) let state = &server.state; if vel.z <= -30.0 { if let Some(stats) = state.ecs().write_storage::().get_mut(entity) { - let falldmg = vel.z.powi(2) as i32 / 20 - 40; + let falldmg = vel.z.powi(2) / 20.0 - 40.0; + let mut damage = Damage { + healthchange: -falldmg, + source: DamageSource::Falling, + }; + if let Some(loadout) = state.ecs().read_storage::().get(entity) { + damage.modify_damage(false, loadout); + } stats.health.change_by(comp::HealthChange { - amount: -falldmg, + amount: damage.healthchange as i32, cause: comp::HealthSource::World, }); } @@ -211,11 +221,12 @@ pub fn handle_explosion(server: &Server, pos: Vec3, power: f32, owner: Opti // Go through all other entities let hit_range = 3.0 * power; let ecs = &server.state.ecs(); - for (pos_b, ori_b, character_b, stats_b) in ( + for (pos_b, ori_b, character_b, stats_b, loadout_b) in ( &ecs.read_storage::(), &ecs.read_storage::(), ecs.read_storage::().maybe(), &mut ecs.write_storage::(), + ecs.read_storage::().maybe(), ) .join() { @@ -227,21 +238,22 @@ pub fn handle_explosion(server: &Server, pos: Vec3, power: f32, owner: Opti && distance_squared < hit_range.powi(2) { // Weapon gives base damage - let mut dmg = ((1.0 - distance_squared / hit_range.powi(2)) * power * 10.0) as u32; + let dmg = (1.0 - distance_squared / hit_range.powi(2)) * power * 10.0; - if rand::random() { - dmg += 1; - } + let mut damage = Damage { + healthchange: -dmg, + source: DamageSource::Explosion, + }; - // Block - if character_b.map(|c_b| c_b.is_block()).unwrap_or(false) - && ori_b.0.angle_between(pos - pos_b.0) < BLOCK_ANGLE.to_radians() / 2.0 - { - dmg = (dmg as f32 * (1.0 - BLOCK_EFFICIENCY)) as u32 + let block = character_b.map(|c_b| c_b.is_block()).unwrap_or(false) + && ori_b.0.angle_between(pos - pos_b.0) < BLOCK_ANGLE.to_radians() / 2.0; + + if let Some(loadout) = loadout_b { + damage.modify_damage(block, loadout); } stats_b.health.change_by(HealthChange { - amount: -(dmg as i32), + amount: damage.healthchange as i32, cause: HealthSource::Projectile { owner }, }); } diff --git a/server/src/migrations/2020-07-24-191205_fix_projectile_stats/down.sql b/server/src/migrations/2020-07-24-191205_fix_projectile_stats/down.sql new file mode 100644 index 0000000000..291a97c5ce --- /dev/null +++ b/server/src/migrations/2020-07-24-191205_fix_projectile_stats/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/server/src/migrations/2020-07-24-191205_fix_projectile_stats/up.sql b/server/src/migrations/2020-07-24-191205_fix_projectile_stats/up.sql new file mode 100644 index 0000000000..2bf12adfb9 --- /dev/null +++ b/server/src/migrations/2020-07-24-191205_fix_projectile_stats/up.sql @@ -0,0 +1,15 @@ +-- This migration adjusts projectiles in accordance with the changes made in MR 1222 +UPDATE + loadout +SET + items = json_replace( + items, + '$.active_item.ability1.BasicRanged.projectile.hit_entity[0]', + json('{"Damage": -3}'), + '$.active_item.ability2.BasicRanged.projectile.hit_entity[0]', + json('{"Damage": -3}'), + '$.second_item.ability1.BasicRanged.projectile.hit_entity[0]', + json('{"Damage": -3}'), + '$.second_item.ability2.BasicRanged.projectile.hit_entity[0]', + json('{"Damage": -3}') + ); \ No newline at end of file From 827b91d69115f70937b30b159256fe72a1aead9b Mon Sep 17 00:00:00 2001 From: Samuel Keiffer Date: Sun, 26 Jul 2020 03:06:53 +0000 Subject: [PATCH 83/85] Bow charged shot attack --- CHANGELOG.md | 1 + common/src/comp/ability.rs | 45 ++++++ common/src/comp/character_state.rs | 11 +- common/src/comp/inventory/item/tool.rs | 27 ++-- common/src/states/charged_ranged.rs | 192 +++++++++++++++++++++++++ common/src/states/mod.rs | 1 + common/src/sys/character_behavior.rs | 2 + common/src/sys/stats.rs | 3 +- voxygen/src/scene/figure/mod.rs | 26 ++++ 9 files changed, 287 insertions(+), 21 deletions(-) create mode 100644 common/src/states/charged_ranged.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b36a920b0..48b76f69e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -73,6 +73,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Pathfinding is much smoother and pets are cleverer - Animals run/turn at different speeds - Updated windowing library (winit 0.19 -> 0.22) +- Bow M2 is now a charged attack that scales the longer it's held ### Removed diff --git a/common/src/comp/ability.rs b/common/src/comp/ability.rs index 575d83b8c5..56a1ad3eda 100644 --- a/common/src/comp/ability.rs +++ b/common/src/comp/ability.rs @@ -18,6 +18,7 @@ pub enum CharacterAbilityType { BasicMelee, BasicRanged, Boost, + ChargedRanged, DashMelee, BasicBlock, TripleStrike(Stage), @@ -36,6 +37,7 @@ impl From<&CharacterState> for CharacterAbilityType { CharacterState::LeapMelee(_) => Self::LeapMelee, CharacterState::TripleStrike(data) => Self::TripleStrike(data.stage), CharacterState::SpinMelee(_) => Self::SpinMelee, + CharacterState::ChargedRanged(_) => Self::ChargedRanged, _ => Self::BasicMelee, } } @@ -90,6 +92,19 @@ pub enum CharacterAbility { recover_duration: Duration, base_damage: u32, }, + ChargedRanged { + energy_cost: u32, + energy_drain: u32, + initial_damage: u32, + max_damage: u32, + initial_knockback: f32, + max_knockback: f32, + prepare_duration: Duration, + charge_duration: Duration, + recover_duration: Duration, + projectile_body: Body, + projectile_light: Option, + }, } impl CharacterAbility { @@ -131,6 +146,10 @@ impl CharacterAbility { .energy .try_change_by(-(*energy_cost as i32), EnergySource::Ability) .is_ok(), + CharacterAbility::ChargedRanged { energy_cost, .. } => update + .energy + .try_change_by(-(*energy_cost as i32), EnergySource::Ability) + .is_ok(), _ => true, } } @@ -311,6 +330,32 @@ impl From<&CharacterAbility> for CharacterState { * this value can be removed if ability moved to * skillbar */ }), + CharacterAbility::ChargedRanged { + energy_cost: _, + energy_drain, + initial_damage, + max_damage, + initial_knockback, + max_knockback, + prepare_duration, + charge_duration, + recover_duration, + projectile_body, + projectile_light, + } => CharacterState::ChargedRanged(charged_ranged::Data { + exhausted: false, + energy_drain: *energy_drain, + initial_damage: *initial_damage, + max_damage: *max_damage, + initial_knockback: *initial_knockback, + max_knockback: *max_knockback, + prepare_duration: *prepare_duration, + charge_duration: *charge_duration, + charge_timer: Duration::default(), + recover_duration: *recover_duration, + projectile_body: *projectile_body, + projectile_light: *projectile_light, + }), } } } diff --git a/common/src/comp/character_state.rs b/common/src/comp/character_state.rs index ee132711fd..a77263578d 100644 --- a/common/src/comp/character_state.rs +++ b/common/src/comp/character_state.rs @@ -66,6 +66,8 @@ pub enum CharacterState { LeapMelee(leap_melee::Data), /// Spin around, dealing damage to enemies surrounding you SpinMelee(spin_melee::Data), + /// A charged ranged attack (e.g. bow) + ChargedRanged(charged_ranged::Data), } impl CharacterState { @@ -78,7 +80,8 @@ impl CharacterState { | CharacterState::TripleStrike(_) | CharacterState::BasicBlock | CharacterState::LeapMelee(_) - | CharacterState::SpinMelee(_) => true, + | CharacterState::SpinMelee(_) + | CharacterState::ChargedRanged(_) => true, _ => false, } } @@ -90,7 +93,8 @@ impl CharacterState { | CharacterState::DashMelee(_) | CharacterState::TripleStrike(_) | CharacterState::LeapMelee(_) - | CharacterState::SpinMelee(_) => true, + | CharacterState::SpinMelee(_) + | CharacterState::ChargedRanged(_) => true, _ => false, } } @@ -102,7 +106,8 @@ impl CharacterState { | CharacterState::DashMelee(_) | CharacterState::TripleStrike(_) | CharacterState::BasicBlock - | CharacterState::LeapMelee(_) => true, + | CharacterState::LeapMelee(_) + | CharacterState::ChargedRanged(_) => true, _ => false, } } diff --git a/common/src/comp/inventory/item/tool.rs b/common/src/comp/inventory/item/tool.rs index 3c92e7c32b..f5dcefb4fd 100644 --- a/common/src/comp/inventory/item/tool.rs +++ b/common/src/comp/inventory/item/tool.rs @@ -273,25 +273,18 @@ impl Tool { projectile_light: None, projectile_gravity: Some(Gravity(0.2)), }, - BasicRanged { - energy_cost: 350, - holdable: true, - prepare_duration: Duration::from_millis(250), - recover_duration: Duration::from_millis(700), - projectile: Projectile { - hit_solid: vec![projectile::Effect::Stick], - hit_entity: vec![ - projectile::Effect::Damage(-9), - projectile::Effect::Knockback(15.0), - projectile::Effect::RewardEnergy(50), - projectile::Effect::Vanish, - ], - time_left: Duration::from_secs(15), - owner: None, - }, + ChargedRanged { + energy_cost: 0, + energy_drain: 300, + initial_damage: 3, + max_damage: 15, + initial_knockback: 10.0, + max_knockback: 20.0, + prepare_duration: Duration::from_millis(100), + charge_duration: Duration::from_millis(1500), + recover_duration: Duration::from_millis(500), projectile_body: Body::Object(object::Body::Arrow), projectile_light: None, - projectile_gravity: Some(Gravity(0.05)), }, ], Dagger(_) => vec![ diff --git a/common/src/states/charged_ranged.rs b/common/src/states/charged_ranged.rs new file mode 100644 index 0000000000..7014f8de58 --- /dev/null +++ b/common/src/states/charged_ranged.rs @@ -0,0 +1,192 @@ +use crate::{ + comp::{ + projectile, Body, CharacterState, EnergySource, Gravity, LightEmitter, Projectile, + StateUpdate, + }, + event::ServerEvent, + states::utils::*, + sys::character_behavior::*, +}; +use serde::{Deserialize, Serialize}; +use std::time::Duration; + +const MAX_GRAVITY: f32 = 0.2; +const MIN_GRAVITY: f32 = 0.05; + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct Data { + /// Whether the attack fired already + pub exhausted: bool, + /// How much energy is drained per second when charging + pub energy_drain: u32, + /// How much damage is dealt with no charge + pub initial_damage: u32, + /// How much damage is dealt with max charge + pub max_damage: u32, + /// How much knockback there is with no chage + pub initial_knockback: f32, + /// How much knockback there is at max charge + pub max_knockback: f32, + /// How long the weapon needs to be prepared for + pub prepare_duration: Duration, + /// How long it takes to charge the weapon to max damage and knockback + pub charge_duration: Duration, + /// How long the state has been charging + pub charge_timer: Duration, + /// How long the state has until exiting + pub recover_duration: Duration, + /// Projectile information + pub projectile_body: Body, + pub projectile_light: Option, +} + +impl CharacterBehavior for Data { + fn behavior(&self, data: &JoinData) -> StateUpdate { + let mut update = StateUpdate::from(data); + + handle_move(data, &mut update, 0.3); + handle_jump(data, &mut update); + + if self.prepare_duration != Duration::default() { + // Prepare (draw the bow) + update.character = CharacterState::ChargedRanged(Data { + exhausted: self.exhausted, + energy_drain: self.energy_drain, + initial_damage: self.initial_damage, + max_damage: self.max_damage, + initial_knockback: self.initial_knockback, + max_knockback: self.max_knockback, + prepare_duration: self + .prepare_duration + .checked_sub(Duration::from_secs_f32(data.dt.0)) + .unwrap_or_default(), + charge_duration: self.charge_duration, + charge_timer: self.charge_timer, + recover_duration: self.recover_duration, + projectile_body: self.projectile_body, + projectile_light: self.projectile_light, + }); + } else if data.inputs.secondary.is_pressed() + && self.charge_timer < self.charge_duration + && update.energy.current() > 0 + { + // Charge the bow + update.character = CharacterState::ChargedRanged(Data { + exhausted: self.exhausted, + energy_drain: self.energy_drain, + initial_damage: self.initial_damage, + max_damage: self.max_damage, + initial_knockback: self.initial_knockback, + max_knockback: self.max_knockback, + prepare_duration: self.prepare_duration, + charge_timer: self + .charge_timer + .checked_add(Duration::from_secs_f32(data.dt.0)) + .unwrap_or_default(), + charge_duration: self.charge_duration, + recover_duration: self.recover_duration, + projectile_body: self.projectile_body, + projectile_light: self.projectile_light, + }); + + // Consumes energy if there's enough left and RMB is held down + update.energy.change_by( + -(self.energy_drain as f32 * data.dt.0) as i32, + EnergySource::Ability, + ); + } else if data.inputs.secondary.is_pressed() { + // Charge the bow + update.character = CharacterState::ChargedRanged(Data { + exhausted: self.exhausted, + energy_drain: self.energy_drain, + initial_damage: self.initial_damage, + max_damage: self.max_damage, + initial_knockback: self.initial_knockback, + max_knockback: self.max_knockback, + prepare_duration: self.prepare_duration, + charge_timer: self.charge_timer, + charge_duration: self.charge_duration, + recover_duration: self.recover_duration, + projectile_body: self.projectile_body, + projectile_light: self.projectile_light, + }); + + // Consumes energy if there's enough left and RMB is held down + update.energy.change_by( + -(self.energy_drain as f32 * data.dt.0 / 5.0) as i32, + EnergySource::Ability, + ); + } else if !self.exhausted { + let charge_amount = + (self.charge_timer.as_secs_f32() / self.charge_duration.as_secs_f32()).min(1.0); + // Fire + let mut projectile = Projectile { + hit_solid: vec![projectile::Effect::Stick], + hit_entity: vec![ + projectile::Effect::Damage( + -(self.initial_damage as i32 + + (charge_amount * (self.max_damage - self.initial_damage) as f32) + as i32), + ), + projectile::Effect::Knockback( + self.initial_knockback + + charge_amount * (self.max_knockback - self.initial_knockback), + ), + projectile::Effect::Vanish, + ], + time_left: Duration::from_secs(15), + owner: None, + }; + projectile.owner = Some(*data.uid); + update.server_events.push_front(ServerEvent::Shoot { + entity: data.entity, + dir: data.inputs.look_dir, + body: self.projectile_body, + projectile, + light: self.projectile_light, + gravity: Some(Gravity( + MAX_GRAVITY - charge_amount * (MAX_GRAVITY - MIN_GRAVITY), + )), + }); + + update.character = CharacterState::ChargedRanged(Data { + exhausted: true, + energy_drain: self.energy_drain, + initial_damage: self.initial_damage, + max_damage: self.max_damage, + initial_knockback: self.initial_knockback, + max_knockback: self.max_knockback, + prepare_duration: self.prepare_duration, + charge_timer: self.charge_timer, + charge_duration: self.charge_duration, + recover_duration: self.recover_duration, + projectile_body: self.projectile_body, + projectile_light: self.projectile_light, + }); + } else if self.recover_duration != Duration::default() { + // Recovery + update.character = CharacterState::ChargedRanged(Data { + exhausted: self.exhausted, + energy_drain: self.energy_drain, + initial_damage: self.initial_damage, + max_damage: self.max_damage, + initial_knockback: self.initial_knockback, + max_knockback: self.max_knockback, + prepare_duration: self.prepare_duration, + charge_timer: self.charge_timer, + charge_duration: self.charge_duration, + recover_duration: self + .recover_duration + .checked_sub(Duration::from_secs_f32(data.dt.0)) + .unwrap_or_default(), + projectile_body: self.projectile_body, + projectile_light: self.projectile_light, + }); + } else { + // Done + update.character = CharacterState::Wielding; + } + + update + } +} diff --git a/common/src/states/mod.rs b/common/src/states/mod.rs index 35676a2d17..b88bfe7514 100644 --- a/common/src/states/mod.rs +++ b/common/src/states/mod.rs @@ -2,6 +2,7 @@ pub mod basic_block; pub mod basic_melee; pub mod basic_ranged; pub mod boost; +pub mod charged_ranged; pub mod climb; pub mod dance; pub mod dash_melee; diff --git a/common/src/sys/character_behavior.rs b/common/src/sys/character_behavior.rs index 496a3f2207..e79d31aa29 100644 --- a/common/src/sys/character_behavior.rs +++ b/common/src/sys/character_behavior.rs @@ -245,6 +245,7 @@ impl<'a> System<'a> for Sys { CharacterState::DashMelee(data) => data.handle_event(&j, action), CharacterState::LeapMelee(data) => data.handle_event(&j, action), CharacterState::SpinMelee(data) => data.handle_event(&j, action), + CharacterState::ChargedRanged(data) => data.handle_event(&j, action), }; local_emitter.append(&mut state_update.local_events); server_emitter.append(&mut state_update.server_events); @@ -271,6 +272,7 @@ impl<'a> System<'a> for Sys { CharacterState::DashMelee(data) => data.behavior(&j), CharacterState::LeapMelee(data) => data.behavior(&j), CharacterState::SpinMelee(data) => data.behavior(&j), + CharacterState::ChargedRanged(data) => data.behavior(&j), }; local_emitter.append(&mut state_update.local_events); diff --git a/common/src/sys/stats.rs b/common/src/sys/stats.rs index f6621cd34c..3c83d45bb9 100644 --- a/common/src/sys/stats.rs +++ b/common/src/sys/stats.rs @@ -105,7 +105,8 @@ impl<'a> System<'a> for Sys { | CharacterState::LeapMelee { .. } | CharacterState::SpinMelee { .. } | CharacterState::TripleStrike { .. } - | CharacterState::BasicRanged { .. } => { + | CharacterState::BasicRanged { .. } + | CharacterState::ChargedRanged { .. } => { if energy.get_unchecked().regen_rate != 0.0 { energy.get_mut_unchecked().regen_rate = 0.0 } diff --git a/voxygen/src/scene/figure/mod.rs b/voxygen/src/scene/figure/mod.rs index 5cbcf39661..279018068d 100644 --- a/voxygen/src/scene/figure/mod.rs +++ b/voxygen/src/scene/figure/mod.rs @@ -681,6 +681,32 @@ impl FigureMgr { ) } }, + CharacterState::ChargedRanged(data) => { + if data.exhausted { + anim::character::ShootAnimation::update_skeleton( + &target_base, + (active_tool_kind, second_tool_kind, vel.0.magnitude(), time), + state.state_time, + &mut state_animation_rate, + skeleton_attr, + ) + } else { + anim::character::ChargeAnimation::update_skeleton( + &target_base, + ( + active_tool_kind, + second_tool_kind, + vel.0.magnitude(), + ori, + state.last_ori, + time, + ), + state.state_time, + &mut state_animation_rate, + skeleton_attr, + ) + } + }, CharacterState::Boost(_) => { anim::character::AlphaAnimation::update_skeleton( &target_base, From a76d2d37463671b12f797f4c1098ee6d290b29db Mon Sep 17 00:00:00 2001 From: Yusuf Bera Ertan Date: Sun, 26 Jul 2020 12:51:52 +0300 Subject: [PATCH 84/85] tr_TR localization: add missing and update some keys --- assets/voxygen/i18n/tr_TR.ron | 65 +++++++++++++++++++++++++++++++---- 1 file changed, 58 insertions(+), 7 deletions(-) diff --git a/assets/voxygen/i18n/tr_TR.ron b/assets/voxygen/i18n/tr_TR.ron index 5208cc0354..87da2a0ecb 100644 --- a/assets/voxygen/i18n/tr_TR.ron +++ b/assets/voxygen/i18n/tr_TR.ron @@ -64,12 +64,12 @@ VoxygenLocalization( "common.back": "Geri", "common.create": "Oluştur", "common.okay": "Tamam", + "common.accept": "Kabul Et", "common.disclaimer": "Uyarı", "common.cancel": "İptal Et", "common.none": "Yok", "common.error": "Hata", "common.fatal_error": "Ölümcül hata", - "common.accept": "Kabul Et", // Message when connection to the server is lost "common.connection_lost": r#"Bağlantı koptu! @@ -95,6 +95,7 @@ Sunucu yeniden mi başladı? /// Start Main screen section "main.connecting": "Bağlanılıyor", "main.creating_world": "Dünya oluşturuluyor", + "main.tip": "İpucu:", // Welcome notice that appears the first time Veloren is started "main.notice": r#"Veloren Alfa sürümüne hoşgeldin! @@ -138,6 +139,7 @@ bir hesap oluşturabilirsin."#, "main.login.failed_sending_request": "Kimlik doğrulama sunucusuna istek gönderilemedi", "main.login.invalid_character": "Seçilen karakter geçersiz", "main.login.client_crashed": "İstemci çöktü", + "main.login.not_on_whitelist": "Sunucuya girmek için bir Yönetici tarafından beyaz listeye eklenmen gerekiyor", /// End Main screen section @@ -154,6 +156,19 @@ bir hesap oluşturabilirsin."#, "hud.press_key_to_toggle_keybindings_fmt": "Kontrolleri açmak veya kapamak için {key}'e bas", "hud.press_key_to_toggle_debug_info_fmt": "Hata ayıklama bilgilerini açmak veya kapamak için {key}'e bas", + + // Chat outputs + "hud.chat.online_msg": "[{name}] çevrimiçi oldu.", + "hud.chat.offline_msg": "{name} çevrimdışı oldu.", + "hud.chat.loot_msg": "[{item}] topladın.", + "hud.chat.loot_fail": "Envanterin dolu!", + "hud.chat.goodbye": "Hoşçakal!", + "hud.chat.connection_lost": "Bağlantı koptu. {time} saniye içinde sunucudan atılacaksın.", + + // SCT outputs + "hud.sct.experience": "{amount} Tecrübe", + "hud.sct.block": "BLOKLANDI", + // Respawn message "hud.press_key_to_respawn": r#"Ziyaret ettiğin en son kamp ateşinde yeniden doğmak için {key}'e bas."#, @@ -254,13 +269,16 @@ magically infused items?"#, "hud.settings.cumulated_damage": "Toplam Verilen Hasarı Göster", "hud.settings.incoming_damage": "Alınan Hasarı Tek Tek Göster", "hud.settings.cumulated_incoming_damage": "Toplam Alınan Hasarı Göster", - "hud.settings.speech_bubble_dark_mode": "Konuşma balonu için karanlık tema kullan", + "hud.settings.speech_bubble": "Konuşma balonu", + "hud.settings.speech_bubble_dark_mode": "Konuşma balonunda karanlık tema kullan", + "hud.settings.speech_bubble_icon": "Konuşma balonunda ikon göster", "hud.settings.energybar_numbers": "Enerji çubuğu değerleri", "hud.settings.values": "Sayılar", "hud.settings.percentages": "Yüzdeler", "hud.settings.chat": "Sohbet", "hud.settings.background_transparency": "Arkaplan Şeffaflığı", - "hud.settings.none": "Yok", + "hud.settings.chat_character_name": "Sohbette karakter isimlerini göster", + "hud.settings.loading_tips": "Yükleme ekranı ipuçları", "hud.settings.pan_sensitivity": "Kaydırma Hassaslığı", "hud.settings.zoom_sensitivity": "Büyütme Hassaslığı", @@ -268,6 +286,9 @@ magically infused items?"#, "hud.settings.invert_mouse_y_axis": "Fare Y eksenini ters çevir", "hud.settings.enable_mouse_smoothing": "Kamera kontrolünü yumuşat", "hud.settings.free_look_behavior": "Serbest bakış davranışı", + "hud.settings.auto_walk_behavior": "Otomatik yürüme davranışı", + "hud.settings.stop_auto_walk_on_input": r#"Otomatik yürüyüşü hareket +edince kapat"#, "hud.settings.view_distance": "Görüş Mesafesi", "hud.settings.sprites_view_distance": "Sprite Görüş Mesafesi", @@ -285,6 +306,8 @@ magically infused items?"#, "hud.settings.save_window_size": "Pencere boyutunu kaydet", "hud.settings.awaitingkey": "Bir tuşa bas...", + "hud.settings.unbound": "Atanmamış", + "hud.settings.reset_keybinds": "Varsayılana döndür", "hud.settings.music_volume": "Müzik Sesi", "hud.settings.sound_effect_volume": "Efekt Sesi", @@ -297,9 +320,17 @@ magically infused items?"#, "hud.social.faction": "Klan", "hud.social.play_online_fmt": "{nb_player} oyuncu çevrimiçi", + "hud.crafting": "Üretim", + "hud.crafting.recipes": "Tarifler", + "hud.crafting.ingredients": "Malzemeler:", + "hud.crafting.craft": "Üret", + "hud.crafting.tool_cata": "Gerektiriyor:", + "hud.spell": "Büyü", "hud.free_look_indicator": "Serbest bakış açık", + "hud.auto_walk_indicator": "Otomatik yürüme açık", + /// End HUD section @@ -351,6 +382,8 @@ magically infused items?"#, "gameinput.togglewield": "Kuşan/koy", "gameinput.interact": "Etkileşim", "gameinput.freelook": "Serbest Bakış", + "gameinput.autowalk": "Otomatik Yürüyüş", + "gameinput.dance": "Dans et", /// End GameInput section @@ -365,7 +398,7 @@ silmek istediğinden emin misin?"#, "char_selection.logout": "Çıkış yap", "char_selection.create_new_charater": "Yeni Karakter Oluştur", "char_selection.creating_character": "Karakter oluşturuluyor...", - "char_selection.character_creation": "Karakter Oluşumu", + "char_selection.character_creation": "Karakter Oluşturma", "char_selection.human_default": "İnsan Varsayılanı", "char_selection.level_fmt": "Seviye {level_nb}", @@ -374,11 +407,9 @@ silmek istediğinden emin misin?"#, "char_selection.beard": "Sakal", "char_selection.hair_style": "Saç Stili", "char_selection.hair_color": "Saç Rengi", - "char_selection.chest_color": "Göğüs Rengi", "char_selection.eye_color": "Göz Rengi", "char_selection.skin": "Deri", "char_selection.eyeshape": "Göz Detayları", - "char_selection.eyebrows": "Kaşlar", "char_selection.accessories": "Aksesuarlar", "char_selection.create_info_name": "Karakterinin bir isme ihtiyacı var!", @@ -393,6 +424,8 @@ silmek istediğinden emin misin?"#, Hareket gücü İrade gücü + +Koruma "#, @@ -406,6 +439,24 @@ Hareket gücü }, vector_map: { + "loading.tips": [ + "'G'ye basarak fenerini yak.", + "'F1'e basarak bütün kontrolleri görebilirsin.", + "'/say' veya '/s' yazarak sadece hemen yanındaki oyuncularla konuşabilirsin.", + "'/region' veya '/r' yazarak sadece bir kaç yüz blok içindeki oyuncularla konuşabilirsin.", + "Özel bir mesaj göndermek için '/tell' ve sonra bir oyuncu ismi ile mesajını yaz.", + "Aynı seviyedeki NPCler farklı zorluklara sahip olabilir.", + "Yemek, sandık ve diğer ganimetler için yere bak!", + "Envanterin yemekle mi dolu? Onları kullanarak daha iyi yemek yapmaya çalış!", + "Ne yapabileceğini merak mı ediyorsun? Zindanlar haritada kahverengi bölgeler olarak işaretlenmiştir!", + "Grafikleri sistemin için ayarlamayı unutma. 'N'e basarak ayarları aç.", + "Başkalarıyla oynamak eğlencelidir! 'O'ya basarak kimlerin çevirimiçi olduğunu gör.", + "Can barının yanında kurukafa olan bir NPC senden hayli bir güçlüdür.", + "'J'ye basarak dans et. Parti!", + "'L-Shift'e basarak Planörünü aç ve gökyüzünü fethet.", + "Veloren hala Pre-Alpha'da. Onu geliştirmek için her gün elimizden geleni yapıyoruz!", + "Geliştirme Takımına katılmak istiyorsan veya sadece sohbet etmek istiyorsan Discord sunucumuza katıl.", + ], "npc.speech.villager_under_attack": [ "Saldırı altındayım, yardım edin!", "Saldırı altındayım! Yardım edin!", @@ -442,7 +493,7 @@ Hareket gücü "Benim için geliyorlar!", "Yardım edin! Yardım edin! Baskı altındayım!", "Ah, artık sistemin doğasında var olan şiddeti görüyoruz.", - "Bu benim için bir çizik bile değil!" + "Bu bana göre bir çizik bile değil!", ], } ) From ba14440a4937f3625670e7112c740db1787285eb Mon Sep 17 00:00:00 2001 From: Imbris Date: Sun, 26 Jul 2020 18:21:20 -0400 Subject: [PATCH 85/85] Fix filtering out resize events in the ui --- voxygen/src/ui/mod.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/voxygen/src/ui/mod.rs b/voxygen/src/ui/mod.rs index 21d490c799..b1a7f941ec 100644 --- a/voxygen/src/ui/mod.rs +++ b/voxygen/src/ui/mod.rs @@ -239,8 +239,10 @@ impl Ui { pub fn handle_event(&mut self, event: Event) { match event.0 { - Input::Resize(w, h) if w > 1.0 && h > 1.0 => { - self.window_resized = Some(Vec2::new(w, h)) + Input::Resize(w, h) => { + if w > 1.0 && h > 1.0 { + self.window_resized = Some(Vec2::new(w, h)) + } }, Input::Touch(touch) => self.ui.handle_event(Input::Touch(Touch { xy: self.scale.scale_point(touch.xy.into()).into_array(), @@ -459,7 +461,7 @@ impl Ui { || image_h == 0 || source_w < 1.0 || source_h < 1.0 - || gl_size.reduce_partial_max() < f32::EPSILON + || gl_size.reduce_partial_min() < f32::EPSILON { None } else {