doshidak / showdex

Pokémon Showdown extension that harnesses the power of parabolic calculus to strategically extract your opponents' Elo.
GNU Affero General Public License v3.0
104 stars 18 forks source link

Teambuilder sets aren't being included in other psim.us sites #105

Closed doshidak closed 1 year ago

doshidak commented 1 year ago

Reported by:

Teambuilder sets aren't being included in other psim.us sites like dl.psim.us (Draft League), most likely due to some cross-origin issue when accessing the showdown_teams key in LocalStorage under https://play.pokemonshowdown.com, which wouldn't be accessible for dl.psim.us.

Need to investigate how the pokemon-showdown-client bypasses this issue since dl.psim.us is able to pull all of my Teambuilder teams from the main Showdown client.

doshidak commented 1 year ago

Upon further inspection, this is how pokemon-showdown-client bypasses the cross-origin issue:

/**
 * Teams are normally loaded from `localStorage`.
 * If the client isn't running on `play.pokemonshowdown.com`, though,
 * teams are received from `crossdomain.php` instead.
 */

Inspecting the response of the https://play.pokemonshowdown.com/crossorigin.php on dl.psim.us with the URL queries:

host=dl.psim.us
path=teambuilder
protocol=https:

the API responds with:

<!DOCTYPE html>
<meta charset="utf-8" />
<script src="/js/lib/jquery-2.1.4.min.js"></script>
<body>
<script>

var configHost = "dlsim.radicalred.net";
var config = "{\"host\":\"dlsim.radicalred.net\",\"https\":true,\"id\":\"dl\",\"port\":443,\"registered\":true}";
var yourOrigin = "https:\/\/dl.psim.us";
var myOrigin = 'https://play.pokemonshowdown.com';

function postReply (message) {
    if (window.parent.postMessage === postReply) return;
    return window.parent.postMessage(message, yourOrigin);
}
function messageHandler(e) {
    if (e.origin !== yourOrigin) return;
    var data = e.data;

    // data's first char:
    // T: store teams
    // P: store prefs
    // R: GET request
    // S: POST request

    switch (data.charAt(0)) {
    case 'T':
        try {
            localStorage.setItem('showdown_teams', data.substr(1));
        } catch (e) {}
        break;
    case 'P':
        try {
            localStorage.setItem('showdown_prefs', data.substr(1));
        } catch (e) {}
        break;
    case 'R':
    case 'S':
        var rq = JSON.parse(data.substr(1));
        $.ajax({
            type: (data.charAt(0) === 'R' ? 'GET' : 'POST'),
            url: rq[0],
            data: rq[1],
            success: function(ajaxdata) {
                postReply('r' + JSON.stringify([rq[2], ajaxdata]));
            },
            dataType: rq[3]
        });
        break;
    }
}

window.addEventListener('message', messageHandler);
if (configHost !== 'showdown') postReply('c' + config);
var storageAvailable = false;
try {
    var testVal = '' + Date.now();
    localStorage.setItem('showdown_allow3p', testVal);
    if (localStorage.getItem('showdown_allow3p') === testVal) {
        postReply('a1');
        postReply('p' + localStorage.getItem('showdown_prefs'));
        postReply('t' + localStorage.getItem('showdown_teams'));
        storageAvailable = true;
    }
} catch (err) {}

if (!storageAvailable) {
    postReply('a0');
}

if (location.protocol + '//' + location.hostname !== myOrigin) {
    // This happens sometimes, but we'll pretend it doesn't
}

</script>

Ideally I wouldn't have to touch this, but rather fortunately (and at the same time, unfortunately), the full list of teams are always available under Storage.teams. Unfortunate being that this is under Storage, which is a keyword for a built-in native Web API, so I'm going to have LOTS of fun wrangling TypeScript.

Storage.teams will include an array of objects with the following schema:

interface StorageTeam {
  capacity: number;
  folder: string;
  format: string;
  gen: GenerationNum;
  iconCache: string;
  name: string;
  team: string;
}

Storage.teams: StorageTeam[]

For each StorageTeam, team is a string that's been formatted like how it is in showdown_teams under LocalStorage, minus the <format>]<name>| part.

Probably the easiest way to go about this is to read Storage.teams, then append the <format>]<name>| from the StorageTeam object, then pass it as normal to the unpackStorageTeams() utility I wrote in v1.1.2 to parse it into CalcdexPokemonPresets.