6 Commits

Author SHA1 Message Date
kuroppoi
0249ff8040 Update submodule 2023-06-29 17:09:35 +02:00
kuroppoi
1133a44360 Exclude unknown items from list view 2023-06-29 17:06:48 +02:00
kuroppoi
71cf227630 Deserialize birth date as string instead of MonthDay object (#4) 2023-06-29 12:46:21 +02:00
kuroppoi
2d346ab9b6 Add visual item list to dashboard 2023-06-29 03:18:41 +02:00
kuroppoi
1ff56e2be0 Add species list view + ability to download available gen V species 2023-06-28 18:20:41 +02:00
kuroppoi
26dbfd0edb Don't URL decode/encode values if base64 decoding/encoding is enabled 2023-06-28 03:47:44 +02:00
12 changed files with 190 additions and 28 deletions

View File

@@ -1,6 +1,7 @@
package entralinked.network.http.dashboard;
import java.util.Collections;
import java.util.Set;
import java.util.stream.Collectors;
import com.fasterxml.jackson.databind.ObjectMapper;
@@ -29,6 +30,10 @@ import io.javalin.json.JavalinJackson;
*/
public class DashboardHandler implements HttpHandler {
private final Set<Integer> availableBlackAndWhiteSpecies = Set.of(
505, 507, 510, 511, 513, 515, 519, 523, 525, 527, 529, 531, 533, 535, 538, 539, 542, 545, 546, 548,
550, 553, 556, 558, 559, 561, 564, 569, 572, 575, 578, 580, 583, 587, 588, 594, 596, 600, 605, 607,
610, 613, 616, 618, 619, 621, 622, 624, 626, 628, 630, 631, 632);
private final DlcList dlcList;
private final PlayerManager playerManager;
@@ -191,8 +196,14 @@ public class DashboardHandler implements HttpHandler {
}
for(DreamEncounter encounter : request.encounters()) {
if(encounter.species() < 1 || encounter.species() > 493) {
if(encounter.species() < 1) {
return "Species is out of range.";
} else if(encounter.species() > 493) {
if(!player.getGameVersion().isVersion2()) {
return "Sorry, Generation V Pokémon are exclusive to Black Version 2 and White Version 2.";
} else if(!availableBlackAndWhiteSpecies.contains(encounter.species())) {
return "You have selected one or more Pokémon species that cannot be downloaded.";
}
} else if(encounter.move() < 1 || encounter.move() > 559) {
return "Move ID is out of range.";
} else if(encounter.gender() == null) {

View File

@@ -1,7 +1,6 @@
package entralinked.network.http.nas;
import java.time.LocalDateTime;
import java.time.MonthDay;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonFormat.Shape;
@@ -24,8 +23,8 @@ public record NasRequest(
@JsonProperty("bssid") String bssid,
@JsonProperty("apinfo") String accessPointInfo,
@JsonProperty("devname") String deviceName,
@JsonProperty("birth") String birthDate, // Hex, apparently
@JsonProperty("devtime") @JsonFormat(shape = Shape.STRING, pattern = "yyMMddHHmmss") LocalDateTime deviceTime,
@JsonProperty("birth") @JsonFormat(shape = Shape.STRING, pattern = "MMdd") MonthDay birthDate,
// Request-specific info
@JsonProperty(value = "action", required = true) String action,

View File

@@ -98,15 +98,14 @@ public class UrlEncodedFormGenerator extends SimpleGeneratorBase {
writer.write('=');
}
String value = text;
// Encode value as base64 if feature is enabled
// Otherwise, encode using URLEncoder.
if(Feature.BASE64_ENCODE_VALUES.enabledIn(formatFeatures)) {
value = Base64.getEncoder().encodeToString(text.getBytes(StandardCharsets.ISO_8859_1))
.replace('=', '*').replace('+', '.').replace('/', '-');
writer.write(Base64.getEncoder().encodeToString(text.getBytes(StandardCharsets.ISO_8859_1))
.replace('=', '*').replace('+', '.').replace('/', '-'));
} else {
writer.write(URLEncoder.encode(text, StandardCharsets.UTF_8));
}
writer.write(URLEncoder.encode(value, StandardCharsets.UTF_8));
}
@Override

View File

@@ -101,13 +101,14 @@ public class UrlEncodedFormParser extends SimpleParserBase {
_currToken = JsonToken.VALUE_STRING;
// Decode base64 if feature is enabled
// Otherwise, decode using URLDecoder.
if(Feature.BASE64_DECODE_VALUES.enabledIn(formatFeatures)) {
parsedString = new String(Base64.getDecoder().decode(
parsedString.replace('*', '=').replace('.', '+').replace('-', '/')), StandardCharsets.ISO_8859_1);
context.setCurrentValue(new String(Base64.getDecoder().decode(
parsedString.replace('*', '=').replace('.', '+').replace('-', '/')), StandardCharsets.ISO_8859_1));
} else {
context.setCurrentValue(URLDecoder.decode(parsedString, StandardCharsets.UTF_8));
}
context.setCurrentValue(URLDecoder.decode(parsedString, StandardCharsets.UTF_8));
if(i != '&') {
if(i != -1) {
_reportUnexpectedChar(i, "expected '&' to mark end of value and start of new key");

View File

@@ -0,0 +1,16 @@
<html>
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="styles/list.css">
</head>
<body onload="fillTableWithItems()">
<div id="main-container" class="root-container">
<label class="big-label">List of items that can be downloaded</label>
<label>NOTE: Item IDs greater than 626 are exclusive to Black Version 2 and White Version 2</label>
<table id="item-table">
<!-- Filled by list.js -->
</table>
</div>
</body>
<script src="scripts/list.js"></script>
</html>

View File

@@ -132,7 +132,7 @@
<div class="content">
<button class="close-button" onclick="closeEncounterForm()">X</button>
<form id="encounter-form">
<label for="encounter-form-species">Species ID</label>
<label for="encounter-form-species">Species ID | </label><a href="/dashboard/species.html" target="_blank">View list</a>
<input id="encounter-form-species" name="species" type="number" value="1" min="1" max="493"/>
<label for="encounter-form-move">Move ID</label>
<input id="encounter-form-move" name="move" type="number" value="1" min="1" max="559"/>
@@ -165,8 +165,8 @@
<div class="content">
<button class="close-button" onclick="closeEncounterForm()">X</button>
<form id="item-form">
<label for="item-form-id">Item ID</label>
<input id="item-form-id" name="id" type="number" value="1" min="1" max="638"/>
<label for="item-form-id">Item ID | </label><a href="/dashboard/items.html" target="_blank">View list</a>
<input id="item-form-id" name="id" type="number" value="1" min="1" max="626"/>
<label for="item-form-quantity">Quantity</label>
<input id="item-form-quantity" name="quantity" type="number" value="1" min="1" max="20"/>
</form>

View File

@@ -0,0 +1,61 @@
const AVAILABLE_GENERATION_V_POKEMON = new Array(
505, 507, 510, 511, 513, 515, 519, 523, 525, 527, 529, 531, 533, 535, 538, 539, 542, 545, 546, 548,
550, 553, 556, 558, 559, 561, 564, 569, 572, 575, 578, 580, 583, 587, 588, 594, 596, 600, 605, 607,
610, 613, 616, 618, 619, 621, 622, 624, 626, 628, 630, 631, 632);
const ITEMS_TO_EXCLUDE = new Array(113, 114, 115, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 426, 427);
function fillTable(tableId, rowSize, elementCount, htmlAppender) {
let tableHTML = "";
let index = 0;
for(let value = 1; value <= elementCount; value++) {
// Call the provided HTML appender to see what should be appended to the table
let toAppend = htmlAppender(value);
// If there is nothing to append (that is, the function has decided this element should be skipped)
// then continue to the next loop.
if(!toAppend) {
continue;
}
// Open a new table row if it should
if(index % rowSize == 0) {
tableHTML += "<tr>";
}
// Append the HTML data
tableHTML += toAppend;
// Close the table row if it has reached the maximum number of elements
if(index % rowSize == rowSize) {
tableHTML += "<tr>";
}
index++;
}
// Set the inner HTML
document.getElementById(tableId).innerHTML = tableHTML;
}
function fillTableWithSpecies() {
fillTable("species-table", 10, 649, (species) => {
// Skip this element if species is from Generation V and cannot be downloaded
if(species > 493 && !AVAILABLE_GENERATION_V_POKEMON.includes(species)) {
return false;
}
return "<td style='width:96px;height:96px;'><image src='/sprites/pokemon/normal/" + species + ".png'/><br>#" + species + "</td>";
});
}
function fillTableWithItems() {
fillTable("item-table", 20, 638, (item) => {
if(ITEMS_TO_EXCLUDE.includes(item)) {
return false;
}
return "<td style='width:48px;height:48px;'><image src='/sprites/items/" + item + ".png'/><br>#" + item + "</td>"
});
}

View File

@@ -45,6 +45,13 @@ function clampValue() {
}
}
// Other constant stuff
const AVAILABLE_GENERATION_V_POKEMON = new Array(
505, 507, 510, 511, 513, 515, 519, 523, 525, 527, 529, 531, 533, 535, 538, 539, 542, 545, 546, 548,
550, 553, 556, 558, 559, 561, 564, 569, 572, 575, 578, 580, 583, 587, 588, 594, 596, 600, 605, 607,
610, 613, 616, 618, 619, 621, 622, 624, 626, 628, 630, 631, 632); // Defining this 3 times is a brilliant idea.
// Local variables
var encounterTableIndex = -1;
var itemTableIndex = -1;
@@ -70,9 +77,17 @@ function saveEncounter() {
return;
}
// Check if this species can be downloaded
let species = parseInt(ELEMENT_ENCOUNTER_SPECIES.value);
if(species > 493 && !AVAILABLE_GENERATION_V_POKEMON.includes(species)) {
alert("This Pokémon species cannot be downloaded. Click 'View list' in the encounter form to view a list of available Pokémon.");
return;
}
// Create encounter data
let encounterData = {
species: ELEMENT_ENCOUNTER_SPECIES.value,
species: species,
move: ELEMENT_ENCOUNTER_MOVE.value,
form: ELEMENT_ENCOUNTER_FORM.value,
gender: ELEMENT_ENCOUNTER_GENDER.value,
@@ -82,16 +97,17 @@ function saveEncounter() {
// Set form to highest form available if it too great
let maxForm = 0;
switch(encounterData.species) {
case "201": maxForm = 27; break; // Unown
case "386": maxForm = 3; break; // Deoxys
case "412":
case "413": maxForm = 2; break; // Burmy & Wormadam
case "422":
case "423":
case "487": maxForm = 1; break; // Shellos, Gastrodon & Giratina
case "479": maxForm = 5; break; // Rotom
case "493": maxForm = 16; break; // Arceus
switch(species) {
case 201: maxForm = 27; break; // Unown
case 386: maxForm = 3; break; // Deoxys
case 412:
case 413: maxForm = 2; break; // Burmy & Wormadam
case 422:
case 423:
case 487: maxForm = 1; break; // Shellos, Gastrodon & Giratina
case 479: maxForm = 5; break; // Rotom
case 493: maxForm = 16; break; // Arceus
case 550: maxForm = 1; break; // Basculin
}
if(encounterData.form > maxForm) {
@@ -271,6 +287,12 @@ function fetchProfileData() {
profile.gameVersion = gameVersion;
ELEMENT_GAME_SUMMARY.innerHTML = "Game Card in use: " + gameVersion;
// Still don't like this!
if(gameVersion.includes("2")) {
ELEMENT_ENCOUNTER_SPECIES.max = 649;
ELEMENT_ITEM_ID.max = 638;
}
// Update dreamer summary
if(dreamerInfo) {
let species = dreamerInfo["species"];

View File

@@ -0,0 +1,16 @@
<html>
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="styles/list.css">
</head>
<body onload="fillTableWithSpecies()">
<div id="main-container" class="root-container"">
<label class="big-label">List of Pokémon species that can be downloaded</label>
<label>NOTE: Generation V Pokémon are exclusive to Black Version 2 and White Version 2</label>
<table id="species-table">
<!-- Filled by list.js -->
</table>
</div>
</body>
<script src="scripts/list.js"></script>
</html>

View File

@@ -0,0 +1,33 @@
body {
background-color: #191919;
color: white;
font-size: 20px;
}
label {
text-align: center;
}
table {
border-spacing: 8px;
margin-top: 15px;
margin-bottom: 20px;
}
td {
background-color: #262626;
border-radius: 10px;
text-align: center;
}
.root-container {
display: grid;
position: absolute;
left: 50%;
transform: translate(-50%, 0%);
width: 1136; /* Hack-ish */
}
.big-label {
font-size: 36px;
}

View File

@@ -35,6 +35,10 @@ button:active {
background-color: #333333;
}
a:link, a:visited {
color: white;
}
.root-container {
position: absolute;
left: 50%;