diff --git a/package.json b/package.json index 17565f3..fa2e1a8 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,7 @@ "mysql-dev": "mysql --host=localhost --user=impress_2020_dev --password=impress_2020_dev --database=impress_2020_dev", "mysql-admin": "mysql --host=impress.openneo.net --user=matchu --password --database=openneo_impress", "mysqldump": "mysqldump --host=impress.openneo.net --user=$(dotenv -p IMPRESS_MYSQL_USER) --password=$(dotenv -p IMPRESS_MYSQL_PASSWORD) --column-statistics=0", - "download-mysql-schema": "yarn --silent mysqldump openneo_impress species species_translations colors color_translations > scripts/setup-mysql-dev-constants.sql && yarn --silent mysqldump --no-data openneo_impress items item_translations > scripts/setup-mysql-dev-schema.sql", + "download-mysql-schema": "yarn --silent mysqldump --no-data openneo_impress items item_translations modeling_logs parents_swf_assets pet_types pet_states swf_assets | sed 's/ AUTO_INCREMENT=[0-9]*//g' > scripts/setup-mysql-dev-schema.sql && yarn --silent mysqldump openneo_impress species species_translations colors color_translations zones zone_translations > scripts/setup-mysql-dev-constants.sql", "setup-mysql": "yarn mysql-admin < scripts/setup-mysql.sql", "setup-mysql-dev": "yarn mysql-dev < scripts/setup-mysql-dev-constants.sql && yarn mysql-dev < scripts/setup-mysql-dev-schema.sql", "build-cached-data": "node -r dotenv/config scripts/build-cached-data.js", diff --git a/scripts/setup-mysql-dev-constants.sql b/scripts/setup-mysql-dev-constants.sql index ba7d6f8..a0993e6 100644 --- a/scripts/setup-mysql-dev-constants.sql +++ b/scripts/setup-mysql-dev-constants.sql @@ -123,6 +123,62 @@ LOCK TABLES `color_translations` WRITE; INSERT INTO `color_translations` VALUES (1,1,'en','alien','2013-01-27 06:10:44','2013-01-27 06:10:44'),(2,2,'en','apple','2013-01-27 06:10:44','2013-01-27 06:10:44'),(3,3,'en','asparagus','2013-01-27 06:10:44','2013-01-27 06:10:44'),(4,4,'en','aubergine','2013-01-27 06:10:44','2013-01-27 06:10:44'),(5,5,'en','avocado','2013-01-27 06:10:44','2013-01-27 06:10:44'),(6,6,'en','baby','2013-01-27 06:10:44','2013-01-27 06:10:44'),(7,7,'en','biscuit','2013-01-27 06:10:44','2013-01-27 06:10:44'),(8,8,'en','blue','2013-01-27 06:10:44','2013-01-27 06:10:44'),(9,9,'en','blueberry','2013-01-27 06:10:44','2013-01-27 06:10:44'),(10,10,'en','brown','2013-01-27 06:10:44','2013-01-27 06:10:44'),(11,11,'en','camouflage','2013-01-27 06:10:44','2013-01-27 06:10:44'),(12,12,'en','carrot','2013-01-27 06:10:44','2013-01-27 06:10:44'),(13,13,'en','checkered','2013-01-27 06:10:44','2013-01-27 06:10:44'),(14,14,'en','chocolate','2013-01-27 06:10:44','2013-01-27 06:10:44'),(15,15,'en','chokato','2013-01-27 06:10:44','2013-01-27 06:10:44'),(16,16,'en','christmas','2013-01-27 06:10:44','2013-01-27 06:10:44'),(17,17,'en','clay','2013-01-27 06:10:44','2013-01-27 06:10:44'),(18,18,'en','cloud','2013-01-27 06:10:44','2013-01-27 06:10:44'),(19,19,'en','coconut','2013-01-27 06:10:44','2013-01-27 06:10:44'),(20,20,'en','custard','2013-01-27 06:10:44','2013-01-27 06:10:44'),(21,21,'en','darigan','2013-01-27 06:10:44','2013-01-27 06:10:44'),(22,22,'en','desert','2013-01-27 06:10:44','2013-01-27 06:10:44'),(23,23,'en','disco','2013-01-27 06:10:44','2013-01-27 06:10:44'),(24,24,'en','durian','2013-01-27 06:10:44','2013-01-27 06:10:44'),(25,25,'en','electric','2013-01-27 06:10:44','2013-01-27 06:10:44'),(26,26,'en','faerie','2013-01-27 06:10:44','2013-01-27 06:10:44'),(27,27,'en','fire','2013-01-27 06:10:44','2013-01-27 06:10:44'),(28,28,'en','garlic','2013-01-27 06:10:44','2013-01-27 06:10:44'),(29,29,'en','ghost','2013-01-27 06:10:44','2013-01-27 06:10:44'),(30,30,'en','glowing','2013-01-27 06:10:44','2013-01-27 06:10:44'),(31,31,'en','gold','2013-01-27 06:10:44','2013-01-27 06:10:44'),(32,32,'en','gooseberry','2013-01-27 06:10:44','2013-01-27 06:10:44'),(33,33,'en','grape','2013-01-27 06:10:44','2013-01-27 06:10:44'),(34,34,'en','green','2013-01-27 06:10:44','2013-01-27 06:10:44'),(35,35,'en','grey','2013-01-27 06:10:44','2013-01-27 06:10:44'),(36,36,'en','halloween','2013-01-27 06:10:44','2013-01-27 06:10:44'),(37,37,'en','ice','2013-01-27 06:10:44','2013-01-27 06:10:44'),(38,38,'en','invisible','2013-01-27 06:10:44','2013-01-27 06:10:44'),(39,39,'en','island','2013-01-27 06:10:44','2013-01-27 06:10:44'),(40,40,'en','jelly','2013-01-27 06:10:44','2013-01-27 06:10:44'),(41,41,'en','lemon','2013-01-27 06:10:44','2013-01-27 06:10:44'),(42,42,'en','lime','2013-01-27 06:10:44','2013-01-27 06:10:44'),(43,43,'en','mallow','2013-01-27 06:10:44','2013-01-27 06:10:44'),(44,44,'en','maraquan','2013-01-27 06:10:44','2013-01-27 06:10:44'),(45,45,'en','msp','2013-01-27 06:10:45','2013-01-27 06:10:45'),(46,46,'en','mutant','2013-01-27 06:10:45','2013-01-27 06:10:45'),(47,47,'en','orange','2013-01-27 06:10:45','2013-01-27 06:10:45'),(48,48,'en','pea','2013-01-27 06:10:45','2013-01-27 06:10:45'),(49,49,'en','peach','2013-01-27 06:10:45','2013-01-27 06:10:45'),(50,50,'en','pear','2013-01-27 06:10:45','2013-01-27 06:10:45'),(51,51,'en','pepper','2013-01-27 06:10:45','2013-01-27 06:10:45'),(52,52,'en','pineapple','2013-01-27 06:10:45','2013-01-27 06:10:45'),(53,53,'en','pink','2013-01-27 06:10:45','2013-01-27 06:10:45'),(54,54,'en','pirate','2013-01-27 06:10:45','2013-01-27 06:10:45'),(55,55,'en','plum','2013-01-27 06:10:45','2013-01-27 06:10:45'),(56,56,'en','plushie','2013-01-27 06:10:45','2013-01-27 06:10:45'),(57,57,'en','purple','2013-01-27 06:10:45','2013-01-27 06:10:45'),(58,58,'en','quigukiboy','2013-01-27 06:10:45','2013-01-27 06:10:45'),(59,59,'en','quigukigirl','2013-01-27 06:10:45','2013-01-27 06:10:45'),(60,60,'en','rainbow','2013-01-27 06:10:45','2013-01-27 06:10:45'),(61,61,'en','red','2013-01-27 06:10:45','2013-01-27 06:10:45'),(62,62,'en','robot','2013-01-27 06:10:45','2013-01-27 06:10:45'),(63,63,'en','royalboy','2013-01-27 06:10:45','2013-01-27 06:10:45'),(64,64,'en','royalgirl','2013-01-27 06:10:45','2013-01-27 06:10:45'),(65,65,'en','shadow','2013-01-27 06:10:45','2013-01-27 06:10:45'),(66,66,'en','silver','2013-01-27 06:10:45','2013-01-27 06:10:45'),(67,67,'en','sketch','2013-01-27 06:10:45','2013-01-27 06:10:45'),(68,68,'en','skunk','2013-01-27 06:10:45','2013-01-27 06:10:45'),(69,69,'en','snot','2013-01-27 06:10:45','2013-01-27 06:10:45'),(70,70,'en','snow','2013-01-27 06:10:45','2013-01-27 06:10:45'),(71,71,'en','speckled','2013-01-27 06:10:45','2013-01-27 06:10:45'),(72,72,'en','split','2013-01-27 06:10:45','2013-01-27 06:10:45'),(73,73,'en','sponge','2013-01-27 06:10:45','2013-01-27 06:10:45'),(74,74,'en','spotted','2013-01-27 06:10:45','2013-01-27 06:10:45'),(75,75,'en','starry','2013-01-27 06:10:45','2013-01-27 06:10:45'),(76,76,'en','strawberry','2013-01-27 06:10:45','2013-01-27 06:10:45'),(77,77,'en','striped','2013-01-27 06:10:45','2013-01-27 06:10:45'),(78,78,'en','thornberry','2013-01-27 06:10:45','2013-01-27 06:10:45'),(79,79,'en','tomato','2013-01-27 06:10:45','2013-01-27 06:10:45'),(80,80,'en','tyrannian','2013-01-27 06:10:45','2013-01-27 06:10:45'),(81,81,'en','usuki boy','2013-01-27 06:10:45','2013-12-21 01:15:29'),(82,82,'en','usuki girl','2013-01-27 06:10:45','2013-12-21 01:15:29'),(83,83,'en','white','2013-01-27 06:10:45','2013-01-27 06:10:45'),(84,84,'en','yellow','2013-01-27 06:10:45','2013-01-27 06:10:45'),(85,85,'en','zombie','2013-01-27 06:10:45','2013-01-27 06:10:45'),(86,86,'en','onion','2013-01-27 06:10:45','2013-01-27 06:10:45'),(87,87,'en','magma','2013-01-27 06:10:45','2013-01-27 06:10:45'),(88,88,'en','relic','2013-01-27 06:10:45','2013-01-27 06:10:45'),(89,89,'en','woodland','2013-01-27 06:10:45','2013-01-27 06:10:45'),(90,90,'en','transparent','2013-01-27 06:10:45','2013-01-27 06:10:45'),(91,91,'en','maractite','2013-01-27 06:10:45','2013-01-27 06:10:45'),(92,92,'en','8-bit','2013-01-27 06:10:45','2013-01-27 06:10:45'),(93,93,'en','swamp gas','2013-01-27 06:10:45','2013-01-27 06:10:45'),(94,94,'en','water','2013-01-27 06:10:45','2013-01-27 06:10:45'),(95,95,'en','wraith','2013-01-27 06:10:45','2013-01-27 06:10:45'),(96,96,'en','eventide','2013-01-27 06:10:45','2013-01-27 06:10:45'),(97,97,'en','elderlyboy','2013-01-27 06:10:45','2013-01-27 06:10:45'),(98,98,'en','elderlygirl','2013-01-27 06:10:45','2013-01-27 06:10:45'),(99,99,'en','stealthy','2013-01-27 06:10:45','2013-01-27 06:10:45'),(100,100,'en','dimensional','2013-01-27 06:10:45','2013-01-27 06:10:45'),(101,94,'pt','Água','2013-01-27 06:11:50','2013-01-27 06:11:50'),(102,92,'pt','8-bit','2013-01-27 06:11:50','2013-01-27 06:11:50'),(103,5,'pt','abacate','2013-01-27 06:11:50','2013-01-27 06:11:50'),(104,52,'pt','abacaxi','2013-01-27 06:11:50','2013-01-27 06:11:50'),(105,28,'pt','alho','2013-01-27 06:11:50','2013-01-27 06:11:50'),(106,1,'pt','alienígena','2013-01-27 06:11:50','2013-01-27 06:11:50'),(107,84,'pt','amarelo','2013-01-27 06:11:50','2013-01-27 06:11:50'),(108,55,'pt','ameixa','2013-01-27 06:11:50','2013-01-27 06:11:50'),(109,60,'pt','arco-íris','2013-01-27 06:11:50','2013-01-27 06:11:50'),(110,17,'pt','argila','2013-01-27 06:11:50','2013-01-27 06:11:50'),(111,3,'pt','aspargos','2013-01-27 06:11:50','2013-01-27 06:11:50'),(112,95,'pt','assombração','2013-01-27 06:11:50','2013-01-27 06:11:50'),(113,8,'pt','azul','2013-01-27 06:11:50','2013-01-27 06:11:50'),(114,6,'pt','bebê','2013-01-27 06:11:50','2013-01-27 06:11:50'),(115,4,'pt','berinjela','2013-01-27 06:11:50','2013-01-27 06:11:50'),(116,7,'pt','biscoito','2013-01-27 06:11:50','2013-01-27 06:11:50'),(117,89,'pt','bosque','2013-01-27 06:11:50','2013-01-27 06:11:50'),(118,83,'pt','branco','2013-01-27 06:11:50','2013-01-27 06:11:50'),(119,30,'pt','brilhante','2013-01-27 06:11:50','2013-01-27 06:11:50'),(120,11,'pt','camuflagem','2013-01-27 06:11:50','2013-01-27 06:11:50'),(121,86,'pt','cebola','2013-01-27 06:11:50','2013-01-27 06:11:50'),(122,12,'pt','cenoura','2013-01-27 06:11:50','2013-01-27 06:11:50'),(123,14,'pt','chocolate','2013-01-27 06:11:50','2013-01-27 06:11:50'),(124,15,'pt','chokato','2013-01-27 06:11:50','2013-01-27 06:11:50'),(125,35,'pt','cinza','2013-01-27 06:11:50','2013-01-27 06:11:50'),(126,19,'pt','coco','2013-01-27 06:11:50','2013-01-27 06:11:50'),(127,20,'pt','creme','2013-01-27 06:11:50','2013-01-27 06:11:50'),(128,96,'pt','crepúsculo','2013-01-27 06:11:50','2013-01-27 06:11:50'),(129,21,'pt','darigan','2013-01-27 06:11:50','2013-01-27 06:11:50'),(130,22,'pt','deserto','2013-01-27 06:11:50','2013-01-27 06:11:50'),(131,100,'pt','dimensional','2013-01-27 06:11:50','2013-01-27 06:11:50'),(132,23,'pt','discoteca','2013-01-27 06:11:50','2013-01-27 06:11:50'),(133,72,'pt','dividido','2013-01-27 06:11:50','2013-01-27 06:11:50'),(134,31,'pt','dourado','2013-01-27 06:11:50','2013-01-27 06:11:50'),(135,24,'pt','durian','2013-01-27 06:11:50','2013-01-27 06:11:50'),(136,25,'pt','elétrico','2013-01-27 06:11:50','2013-01-27 06:11:50'),(137,48,'pt','ervilha','2013-01-27 06:11:50','2013-01-27 06:11:50'),(138,73,'pt','esponja','2013-01-27 06:11:50','2013-01-27 06:11:50'),(139,75,'pt','estrelado','2013-01-27 06:11:50','2013-01-27 06:11:50'),(140,26,'pt','fada','2013-01-27 06:11:50','2013-01-27 06:11:50'),(141,29,'pt','fantasma','2013-01-27 06:11:50','2013-01-27 06:11:50'),(142,27,'pt','fogo','2013-01-27 06:11:50','2013-01-27 06:11:50'),(143,78,'pt','frutathorn','2013-01-27 06:11:50','2013-01-27 06:11:50'),(144,68,'pt','gambá','2013-01-27 06:11:50','2013-01-27 06:11:50'),(145,82,'pt','garota usuki','2013-01-27 06:11:50','2013-01-27 06:11:50'),(146,81,'pt','garoto usuki','2013-01-27 06:11:50','2013-01-27 06:11:50'),(147,93,'pt','gas metano','2013-01-27 06:11:50','2013-01-27 06:11:50'),(148,40,'pt','gelatina','2013-01-27 06:11:50','2013-01-27 06:11:50'),(149,37,'pt','gelo','2013-01-27 06:11:50','2013-01-27 06:11:50'),(150,32,'pt','groselha','2013-01-27 06:11:50','2013-01-27 06:11:50'),(151,36,'pt','halloween','2013-01-27 06:11:50','2013-01-27 06:11:50'),(152,98,'pt','idosa ','2013-01-27 06:11:50','2013-01-27 06:11:50'),(153,97,'pt','idoso ','2013-01-27 06:11:50','2013-01-27 06:11:50'),(154,39,'pt','ilha','2013-01-27 06:11:50','2013-01-27 06:11:50'),(155,38,'pt','invisível','2013-01-27 06:11:50','2013-01-27 06:11:50'),(156,47,'pt','laranja','2013-01-27 06:11:50','2013-01-27 06:11:50'),(157,41,'pt','limão','2013-01-27 06:11:50','2013-01-27 06:11:50'),(158,42,'pt','lima','2013-01-27 06:11:50','2013-01-27 06:11:50'),(159,77,'pt','listrado','2013-01-27 06:11:50','2013-01-27 06:11:50'),(160,2,'pt','maçã','2013-01-27 06:11:51','2013-01-27 06:11:51'),(161,87,'pt','magma','2013-01-27 06:11:51','2013-01-27 06:11:51'),(162,71,'pt','manchado','2013-01-27 06:11:51','2013-01-27 06:11:51'),(163,91,'pt','maractita','2013-01-27 06:11:51','2013-01-27 06:11:51'),(164,44,'pt','maraquano','2013-01-27 06:11:51','2013-01-27 06:11:51'),(165,10,'pt','marrom','2013-01-27 06:11:51','2013-01-27 06:11:51'),(166,43,'pt','marshmallow','2013-01-27 06:11:51','2013-01-27 06:11:51'),(167,69,'pt','meleca','2013-01-27 06:11:51','2013-01-27 06:11:51'),(168,9,'pt','mirtilo','2013-01-27 06:11:51','2013-01-27 06:11:51'),(169,76,'pt','morango','2013-01-27 06:11:51','2013-01-27 06:11:51'),(170,45,'pt','msp','2013-01-27 06:11:51','2013-01-27 06:11:51'),(171,46,'pt','mutante','2013-01-27 06:11:51','2013-01-27 06:11:51'),(172,16,'pt','natal','2013-01-27 06:11:51','2013-01-27 06:11:51'),(173,70,'pt','neve','2013-01-27 06:11:51','2013-01-27 06:11:51'),(174,18,'pt','nuvem','2013-01-27 06:11:51','2013-01-27 06:11:51'),(175,50,'pt','pêra','2013-01-27 06:11:51','2013-01-27 06:11:51'),(176,49,'pt','pêssego','2013-01-27 06:11:51','2013-01-27 06:11:51'),(177,56,'pt','pelúcia','2013-01-27 06:11:51','2013-01-27 06:11:51'),(178,51,'pt','pimenta','2013-01-27 06:11:51','2013-01-27 06:11:51'),(179,74,'pt','pintado','2013-01-27 06:11:51','2013-01-27 06:11:51'),(180,54,'pt','pirata','2013-01-27 06:11:51','2013-01-27 06:11:51'),(181,66,'pt','prata','2013-01-27 06:11:51','2013-01-27 06:11:51'),(182,59,'pt','quiguki fêmea','2013-01-27 06:11:51','2013-01-27 06:11:51'),(183,58,'pt','quiguki macho','2013-01-27 06:11:51','2013-01-27 06:11:51'),(184,67,'pt','rascunho','2013-01-27 06:11:51','2013-01-27 06:11:51'),(185,64,'pt','real fêmea','2013-01-27 06:11:51','2013-01-27 06:11:51'),(186,63,'pt','real macho','2013-01-27 06:11:51','2013-01-27 06:11:51'),(187,88,'pt','relíquia','2013-01-27 06:11:51','2013-01-27 06:11:51'),(188,62,'pt','robô','2013-01-27 06:11:51','2013-01-27 06:11:51'),(189,53,'pt','rosa','2013-01-27 06:11:51','2013-01-27 06:11:51'),(190,57,'pt','roxo','2013-01-27 06:11:51','2013-01-27 06:11:51'),(191,99,'pt','sigiloso','2013-01-27 06:11:51','2013-01-27 06:11:51'),(192,65,'pt','sombra','2013-01-27 06:11:51','2013-01-27 06:11:51'),(193,79,'pt','tomate','2013-01-27 06:11:51','2013-01-27 06:11:51'),(194,90,'pt','transparente','2013-01-27 06:11:51','2013-01-27 06:11:51'),(195,80,'pt','tyranniano','2013-01-27 06:11:51','2013-01-27 06:11:51'),(196,33,'pt','uva','2013-01-27 06:11:51','2013-01-27 06:11:51'),(197,34,'pt','verde','2013-01-27 06:11:51','2013-01-27 06:11:51'),(198,61,'pt','vermelho','2013-01-27 06:11:51','2013-01-27 06:11:51'),(199,13,'pt','xadrez','2013-01-27 06:11:51','2013-01-27 06:11:51'),(200,85,'pt','zumbi','2013-01-27 06:11:51','2013-01-27 06:11:51'),(201,92,'en-MEEP','8-bit','2013-01-28 02:46:52','2013-01-28 02:46:52'),(202,1,'en-MEEP','alien','2013-01-28 02:46:52','2013-01-28 02:46:52'),(203,2,'en-MEEP','apple','2013-01-28 02:46:52','2013-01-28 02:46:52'),(204,3,'en-MEEP','asparagus','2013-01-28 02:46:52','2013-01-28 02:46:52'),(205,4,'en-MEEP','aubergine','2013-01-28 02:46:52','2013-01-28 02:46:52'),(206,5,'en-MEEP','avocado','2013-01-28 02:46:52','2013-01-28 02:46:52'),(207,6,'en-MEEP','baby','2013-01-28 02:46:52','2013-01-28 02:46:52'),(208,7,'en-MEEP','biscuit','2013-01-28 02:46:52','2013-01-28 02:46:52'),(209,8,'en-MEEP','blue','2013-01-28 02:46:52','2013-01-28 02:46:52'),(210,9,'en-MEEP','blueberry','2013-01-28 02:46:52','2013-01-28 02:46:52'),(211,10,'en-MEEP','brown','2013-01-28 02:46:52','2013-01-28 02:46:52'),(212,11,'en-MEEP','camouflage','2013-01-28 02:46:52','2013-01-28 02:46:52'),(213,12,'en-MEEP','carrot','2013-01-28 02:46:52','2013-01-28 02:46:52'),(214,13,'en-MEEP','checkered','2013-01-28 02:46:52','2013-01-28 02:46:52'),(215,14,'en-MEEP','chocolate','2013-01-28 02:46:52','2013-01-28 02:46:52'),(216,15,'en-MEEP','chokato','2013-01-28 02:46:52','2013-01-28 02:46:52'),(217,16,'en-MEEP','christmas','2013-01-28 02:46:52','2013-01-28 02:46:52'),(218,17,'en-MEEP','clay','2013-01-28 02:46:52','2013-01-28 02:46:52'),(219,18,'en-MEEP','cloud','2013-01-28 02:46:52','2013-01-28 02:46:52'),(220,19,'en-MEEP','coconut','2013-01-28 02:46:52','2013-01-28 02:46:52'),(221,20,'en-MEEP','custard','2013-01-28 02:46:52','2013-01-28 02:46:52'),(222,21,'en-MEEP','darigan','2013-01-28 02:46:52','2013-01-28 02:46:52'),(223,22,'en-MEEP','desert','2013-01-28 02:46:52','2013-01-28 02:46:53'),(224,100,'en-MEEP','dimensional','2013-01-28 02:46:53','2013-01-28 02:46:53'),(225,23,'en-MEEP','disco','2013-01-28 02:46:53','2013-01-28 02:46:53'),(226,24,'en-MEEP','durian','2013-01-28 02:46:53','2013-01-28 02:46:53'),(227,97,'en-MEEP','elderlyboy','2013-01-28 02:46:53','2013-01-28 02:46:53'),(228,98,'en-MEEP','elderlygirl','2013-01-28 02:46:53','2013-01-28 02:46:53'),(229,25,'en-MEEP','electric','2013-01-28 02:46:53','2013-01-28 02:46:53'),(230,96,'en-MEEP','eventide','2013-01-28 02:46:53','2013-01-28 02:46:53'),(231,26,'en-MEEP','faerie','2013-01-28 02:46:53','2013-01-28 02:46:53'),(232,27,'en-MEEP','fire','2013-01-28 02:46:53','2013-01-28 02:46:53'),(233,28,'en-MEEP','garlic','2013-01-28 02:46:53','2013-01-28 02:46:53'),(234,29,'en-MEEP','ghost','2013-01-28 02:46:53','2013-01-28 02:46:53'),(235,30,'en-MEEP','glowing','2013-01-28 02:46:53','2013-01-28 02:46:53'),(236,31,'en-MEEP','gold','2013-01-28 02:46:53','2013-01-28 02:46:53'),(237,32,'en-MEEP','gooseberry','2013-01-28 02:46:53','2013-01-28 02:46:53'),(238,33,'en-MEEP','grape','2013-01-28 02:46:53','2013-01-28 02:46:53'),(239,34,'en-MEEP','green','2013-01-28 02:46:53','2013-01-28 02:46:53'),(240,35,'en-MEEP','grey','2013-01-28 02:46:53','2013-01-28 02:46:53'),(241,36,'en-MEEP','halloween','2013-01-28 02:46:53','2013-01-28 02:46:53'),(242,37,'en-MEEP','ice','2013-01-28 02:46:53','2013-01-28 02:46:53'),(243,38,'en-MEEP','invisible','2013-01-28 02:46:53','2013-01-28 02:46:53'),(244,39,'en-MEEP','island','2013-01-28 02:46:53','2013-01-28 02:46:53'),(245,40,'en-MEEP','jelly','2013-01-28 02:46:53','2013-01-28 02:46:53'),(246,41,'en-MEEP','lemon','2013-01-28 02:46:53','2013-01-28 02:46:53'),(247,42,'en-MEEP','lime','2013-01-28 02:46:53','2013-01-28 02:46:53'),(248,87,'en-MEEP','magma','2013-01-28 02:46:53','2013-01-28 02:46:53'),(249,43,'en-MEEP','mallow','2013-01-28 02:46:53','2013-01-28 02:46:53'),(250,91,'en-MEEP','maractite','2013-01-28 02:46:53','2013-01-28 02:46:53'),(251,44,'en-MEEP','maraquan','2013-01-28 02:46:53','2013-01-28 02:46:53'),(252,45,'en-MEEP','msp','2013-01-28 02:46:53','2013-01-28 02:46:53'),(253,46,'en-MEEP','mutant','2013-01-28 02:46:53','2013-01-28 02:46:53'),(254,86,'en-MEEP','onion','2013-01-28 02:46:53','2013-01-28 02:46:53'),(255,47,'en-MEEP','orange','2013-01-28 02:46:53','2013-01-28 02:46:53'),(256,48,'en-MEEP','pea','2013-01-28 02:46:53','2013-01-28 02:46:53'),(257,49,'en-MEEP','peach','2013-01-28 02:46:53','2013-01-28 02:46:53'),(258,50,'en-MEEP','pear','2013-01-28 02:46:53','2013-01-28 02:46:53'),(259,51,'en-MEEP','pepper','2013-01-28 02:46:53','2013-01-28 02:46:53'),(260,52,'en-MEEP','pineapple','2013-01-28 02:46:53','2013-01-28 02:46:53'),(261,53,'en-MEEP','pink','2013-01-28 02:46:53','2013-01-28 02:46:53'),(262,54,'en-MEEP','pirate','2013-01-28 02:46:53','2013-01-28 02:46:53'),(263,55,'en-MEEP','plum','2013-01-28 02:46:53','2013-01-28 02:46:53'),(264,56,'en-MEEP','plushie','2013-01-28 02:46:53','2013-01-28 02:46:53'),(265,57,'en-MEEP','purple','2013-01-28 02:46:53','2013-01-28 02:46:53'),(266,58,'en-MEEP','quigukiboy','2013-01-28 02:46:53','2013-01-28 02:46:53'),(267,59,'en-MEEP','quigukigirl','2013-01-28 02:46:53','2013-01-28 02:46:53'),(268,60,'en-MEEP','rainbow','2013-01-28 02:46:53','2013-01-28 02:46:53'),(269,61,'en-MEEP','red','2013-01-28 02:46:53','2013-01-28 02:46:53'),(270,88,'en-MEEP','relic','2013-01-28 02:46:53','2013-01-28 02:46:53'),(271,62,'en-MEEP','robot','2013-01-28 02:46:53','2013-01-28 02:46:53'),(272,63,'en-MEEP','royalboy','2013-01-28 02:46:53','2013-01-28 02:46:53'),(273,64,'en-MEEP','royalgirl','2013-01-28 02:46:53','2013-01-28 02:46:53'),(274,65,'en-MEEP','shadow','2013-01-28 02:46:53','2013-01-28 02:46:53'),(275,66,'en-MEEP','silver','2013-01-28 02:46:53','2013-01-28 02:46:53'),(276,67,'en-MEEP','sketch','2013-01-28 02:46:53','2013-01-28 02:46:53'),(277,68,'en-MEEP','skunk','2013-01-28 02:46:53','2013-01-28 02:46:53'),(278,69,'en-MEEP','snot','2013-01-28 02:46:53','2013-01-28 02:46:53'),(279,70,'en-MEEP','snow','2013-01-28 02:46:53','2013-01-28 02:46:53'),(280,71,'en-MEEP','speckled','2013-01-28 02:46:53','2013-01-28 02:46:53'),(281,72,'en-MEEP','split','2013-01-28 02:46:53','2013-01-28 02:46:53'),(282,73,'en-MEEP','sponge','2013-01-28 02:46:53','2013-01-28 02:46:53'),(283,74,'en-MEEP','spotted','2013-01-28 02:46:53','2013-01-28 02:46:53'),(284,75,'en-MEEP','starry','2013-01-28 02:46:53','2013-01-28 02:46:53'),(285,99,'en-MEEP','stealthy','2013-01-28 02:46:53','2013-01-28 02:46:53'),(286,76,'en-MEEP','strawberry','2013-01-28 02:46:53','2013-01-28 02:46:53'),(287,77,'en-MEEP','striped','2013-01-28 02:46:53','2013-01-28 02:46:53'),(288,93,'en-MEEP','swamp gas','2013-01-28 02:46:53','2013-01-28 02:46:53'),(289,78,'en-MEEP','thornberry','2013-01-28 02:46:53','2013-01-28 02:46:53'),(290,79,'en-MEEP','tomato','2013-01-28 02:46:53','2013-01-28 02:46:53'),(291,90,'en-MEEP','transparent','2013-01-28 02:46:53','2013-01-28 02:46:53'),(292,80,'en-MEEP','tyrannian','2013-01-28 02:46:53','2013-01-28 02:46:53'),(293,81,'en-MEEP','usuki boy','2013-01-28 02:46:53','2013-01-28 02:46:53'),(294,82,'en-MEEP','usuki girl','2013-01-28 02:46:53','2013-01-28 02:46:53'),(295,94,'en-MEEP','water','2013-01-28 02:46:53','2013-01-28 02:46:53'),(296,83,'en-MEEP','white','2013-01-28 02:46:53','2013-01-28 02:46:53'),(297,89,'en-MEEP','woodland','2013-01-28 02:46:53','2013-01-28 02:46:53'),(298,95,'en-MEEP','wraith','2013-01-28 02:46:53','2013-01-28 02:46:53'),(299,84,'en-MEEP','yellow','2013-01-28 02:46:53','2013-01-28 02:46:53'),(300,85,'en-MEEP','zombie','2013-01-28 02:46:53','2013-01-28 02:46:53'),(301,92,'es','8-bits','2013-02-12 05:33:20','2013-02-12 05:33:20'),(302,13,'es','a cuadros','2013-02-12 05:33:20','2013-02-12 05:33:20'),(303,94,'es','agua','2013-02-12 05:33:20','2013-02-12 05:33:20'),(304,5,'es','aguacate','2013-02-12 05:33:20','2013-02-12 05:33:20'),(305,28,'es','ajo','2013-02-12 05:33:20','2013-02-12 05:33:20'),(306,1,'es','alienígena','2013-02-12 05:33:20','2013-02-12 05:33:20'),(307,84,'es','amarillo','2013-02-12 05:33:20','2013-02-12 05:33:20'),(308,98,'es','anciana','2013-02-12 05:33:20','2013-02-12 05:33:20'),(309,97,'es','anciano','2013-02-12 05:33:20','2013-02-12 05:33:20'),(310,96,'es','anochecer','2013-02-12 05:33:20','2013-02-12 05:33:20'),(311,77,'es','a rayas','2013-02-12 05:33:20','2013-02-12 05:33:20'),(312,17,'es','arcilla ','2013-02-12 05:33:20','2013-02-12 05:33:20'),(313,60,'es','arco iris','2013-02-12 05:33:20','2013-02-12 05:33:20'),(314,8,'es','azul','2013-02-12 05:33:20','2013-02-12 05:33:20'),(315,25,'es','azul eléctrico','2013-02-12 05:33:20','2013-02-12 05:33:20'),(316,6,'es','bebé','2013-02-12 05:33:20','2013-02-12 05:33:20'),(317,4,'es','berenjena','2013-02-12 05:33:20','2013-02-12 05:33:20'),(318,83,'es','blanco','2013-02-12 05:33:21','2013-02-12 05:33:21'),(319,89,'es','bosque','2013-02-12 05:33:21','2013-02-12 05:33:21'),(320,11,'es','camuflaje','2013-02-12 05:33:21','2013-02-12 05:33:21'),(321,86,'es','cebolla','2013-02-12 05:33:21','2013-02-12 05:33:21'),(322,14,'es','chocolate','2013-02-12 05:33:21','2013-02-12 05:33:21'),(323,15,'es','chokato','2013-02-12 05:33:21','2013-02-12 05:33:21'),(324,55,'es','ciruela','2013-02-12 05:33:21','2013-02-12 05:33:21'),(325,19,'es','coco','2013-02-12 05:33:21','2013-02-12 05:33:21'),(326,71,'es','con manchas','2013-02-12 05:33:21','2013-02-12 05:33:21'),(327,74,'es','con motas','2013-02-12 05:33:21','2013-02-12 05:33:21'),(328,21,'es','darigan','2013-02-12 05:33:21','2013-02-12 05:33:21'),(329,22,'es','desierto','2013-02-12 05:33:21','2013-02-12 05:33:21'),(330,67,'es','dibujo','2013-02-12 05:33:21','2013-02-12 05:33:21'),(331,100,'es','dimensional','2013-02-12 05:33:21','2013-02-12 05:33:21'),(332,23,'es','disco','2013-02-12 05:33:21','2013-02-12 05:33:21'),(333,72,'es','dividido','2013-02-12 05:33:21','2013-02-12 05:33:21'),(334,31,'es','dorado','2013-02-12 05:33:21','2013-02-12 05:33:21'),(335,24,'es','durian','2013-02-12 05:33:21','2013-02-12 05:33:21'),(336,3,'es','espárrago','2013-02-12 05:33:21','2013-02-12 05:33:21'),(337,95,'es','espíritu','2013-02-12 05:33:21','2013-02-12 05:33:21'),(338,78,'es','espinabaya','2013-02-12 05:33:21','2013-02-12 05:33:21'),(339,73,'es','esponja','2013-02-12 05:33:21','2013-02-12 05:33:21'),(340,75,'es','estrellado','2013-02-12 05:33:21','2013-02-12 05:33:21'),(341,29,'es','fantasma','2013-02-12 05:33:21','2013-02-12 05:33:21'),(342,20,'es','flan','2013-02-12 05:33:21','2013-02-12 05:33:21'),(343,30,'es','fluorescente','2013-02-12 05:33:21','2013-02-12 05:33:21'),(344,76,'es','fresa','2013-02-12 05:33:21','2013-02-12 05:33:21'),(345,27,'es','fuego','2013-02-12 05:33:21','2013-02-12 05:33:21'),(346,7,'es','galleta','2013-02-12 05:33:21','2013-02-12 05:33:21'),(347,32,'es','gansobaya','2013-02-12 05:33:21','2013-02-12 05:33:21'),(348,40,'es','gelatina','2013-02-12 05:33:21','2013-02-12 05:33:21'),(349,35,'es','gris','2013-02-12 05:33:21','2013-02-12 05:33:21'),(350,48,'es','guisante','2013-02-12 05:33:21','2013-02-12 05:33:21'),(351,26,'es','hada','2013-02-12 05:33:21','2013-02-12 05:33:21'),(352,36,'es','halloween','2013-02-12 05:33:21','2013-02-12 05:33:21'),(353,37,'es','hielo','2013-02-12 05:33:21','2013-02-12 05:33:21'),(354,38,'es','invisible','2013-02-12 05:33:21','2013-02-12 05:33:21'),(355,39,'es','isla','2013-02-12 05:33:21','2013-02-12 05:33:21'),(356,41,'es','limón','2013-02-12 05:33:21','2013-02-12 05:33:21'),(357,42,'es','limón','2013-02-12 05:33:21','2013-02-12 05:33:21'),(358,87,'es','magma','2013-02-12 05:33:21','2013-02-12 05:33:21'),(359,2,'es','manzana','2013-02-12 05:33:21','2013-02-12 05:33:21'),(360,91,'es','maractita','2013-02-12 05:33:21','2013-02-12 05:33:21'),(361,44,'es','maracuático','2013-02-12 05:33:21','2013-02-12 05:33:21'),(362,10,'es','marrón','2013-02-12 05:33:21','2013-02-12 05:33:21'),(363,43,'es','marshmallow','2013-02-12 05:33:21','2013-02-12 05:33:21'),(364,49,'es','melocotón','2013-02-12 05:33:21','2013-02-12 05:33:21'),(365,93,'es','metano','2013-02-12 05:33:21','2013-02-12 05:33:21'),(366,69,'es','moco','2013-02-12 05:33:21','2013-02-12 05:33:21'),(367,68,'es','mofeta','2013-02-12 05:33:21','2013-02-12 05:33:21'),(368,9,'es','mora azul','2013-02-12 05:33:21','2013-02-12 05:33:21'),(369,57,'es','morado','2013-02-12 05:33:21','2013-02-12 05:33:21'),(370,45,'es','msp','2013-02-12 05:33:21','2013-02-12 05:33:21'),(371,46,'es','mutante','2013-02-12 05:33:21','2013-02-12 05:33:21'),(372,47,'es','naranja','2013-02-12 05:33:21','2013-02-12 05:33:21'),(373,16,'es','navidad','2013-02-12 05:33:21','2013-02-12 05:33:21'),(374,64,'es','niña de la realeza','2013-02-12 05:33:21','2013-02-12 05:33:21'),(375,63,'es','niño de la realeza','2013-02-12 05:33:22','2013-02-12 05:33:22'),(376,70,'es','nieve','2013-02-12 05:33:22','2013-02-12 05:33:22'),(377,18,'es','nube','2013-02-12 05:33:22','2013-02-12 05:33:22'),(378,56,'es','peluche','2013-02-12 05:33:22','2013-02-12 05:33:22'),(379,50,'es','pera','2013-02-12 05:33:22','2013-02-12 05:33:22'),(380,52,'es','piña','2013-02-12 05:33:22','2013-02-12 05:33:22'),(381,51,'es','pimiento','2013-02-12 05:33:22','2013-02-12 05:33:22'),(382,54,'es','pirata','2013-02-12 05:33:22','2013-02-12 05:33:22'),(383,66,'es','plateado','2013-02-12 05:33:22','2013-02-12 05:33:22'),(384,59,'es','quiguki niña','2013-02-12 05:33:22','2013-02-12 05:33:22'),(385,58,'es','quiguki niño','2013-02-12 05:33:22','2013-02-12 05:33:22'),(386,88,'es','reliquia','2013-02-12 05:33:22','2013-02-12 05:33:22'),(387,62,'es','robot','2013-02-12 05:33:22','2013-02-12 05:33:22'),(388,61,'es','rojo','2013-02-12 05:33:22','2013-02-12 05:33:22'),(389,53,'es','rosa','2013-02-12 05:33:22','2013-02-12 05:33:22'),(390,99,'es','sigiloso','2013-02-12 05:33:22','2013-02-12 05:33:22'),(391,65,'es','sombra','2013-02-12 05:33:22','2013-02-12 05:33:22'),(392,79,'es','tomate','2013-02-12 05:33:22','2013-02-12 05:33:22'),(393,90,'es','transparente','2013-02-12 05:33:22','2013-02-12 05:33:22'),(394,80,'es','tyranniano','2013-02-12 05:33:22','2013-02-12 05:33:22'),(395,82,'es','usuki niña','2013-02-12 05:33:22','2013-02-12 05:33:22'),(396,81,'es','usuki niño','2013-02-12 05:33:22','2013-02-12 05:33:22'),(397,33,'es','uva','2013-02-12 05:33:22','2013-02-12 05:33:22'),(398,34,'es','verde','2013-02-12 05:33:22','2013-02-12 05:33:22'),(399,12,'es','zanahoria ','2013-02-12 05:33:22','2013-02-12 05:33:22'),(400,85,'es','zombi','2013-02-12 05:33:22','2013-02-12 05:33:22'),(401,101,'en','agueena','2013-02-16 06:01:08','2013-12-21 01:15:19'),(402,101,'pt','Agueena','2013-02-16 06:03:06','2013-02-16 06:03:06'),(403,101,'es','Agüina','2013-02-16 06:03:06','2013-02-16 06:03:06'),(404,102,'en','pastel','2013-12-21 01:19:37','2014-11-15 01:52:51'),(405,-1,'en','nebula','2014-04-01 03:18:20','2014-04-01 03:18:20'),(406,103,'en','ummagine','2014-10-03 21:54:58','2014-11-15 01:54:18'),(407,-1,'pt','nebula','2014-11-15 01:50:34','2014-11-15 01:50:34'),(408,-1,'es','nebula','2014-11-15 01:50:34','2014-11-15 01:50:34'),(409,-1,'en-MEEP','nebula','2014-11-15 01:50:34','2014-11-15 01:50:34'),(410,101,'en-MEEP','agueena','2014-11-15 01:51:08','2014-11-15 01:51:08'),(411,102,'en-MEEP','pastel','2014-11-15 01:52:51','2014-11-15 01:52:51'),(412,102,'pt','pastel','2014-11-15 01:52:51','2014-11-15 01:52:51'),(413,102,'es','pastel','2014-11-15 01:52:51','2014-11-15 01:52:51'),(414,103,'es','rarajena','2014-11-15 01:54:18','2014-11-15 01:54:18'),(415,103,'pt','ummagina','2014-11-15 01:54:18','2014-11-15 01:54:18'),(416,103,'en-MEEP','ummagine','2014-11-15 01:54:18','2014-11-15 01:54:18'),(417,104,'en','Polka Dot','2015-02-12 02:29:50','2015-02-12 02:29:50'),(418,104,'en-MEEP','Polka Dot','2015-02-12 02:31:47','2015-02-12 02:31:47'),(419,104,'pt','Polka Dot','2015-02-12 02:31:47','2015-02-12 02:31:47'),(420,104,'es','Polka Dot','2015-02-12 02:31:47','2015-02-12 02:31:47'),(421,105,'en','Candy','2017-02-23 19:19:07','2017-02-23 19:19:07'),(422,106,'en','marble','2017-02-23 19:29:00','2017-02-23 19:29:00'),(423,107,'en','Steampunk','2018-01-27 23:54:10','2018-01-27 23:54:10'),(424,108,'en','Toy','2018-02-14 22:40:32','2018-02-14 22:40:32'),(425,109,'en','Origami','2018-03-07 04:53:11','2018-03-07 04:53:11'),(426,110,'en','Oil Paint','2019-03-07 03:15:31','2019-03-07 03:20:36'),(427,111,'en','Mosaic','2019-04-29 18:04:39','2019-04-29 18:04:39'),(428,112,'en','Burlap','2019-04-29 18:06:47','2019-04-29 18:06:47'); /*!40000 ALTER TABLE `color_translations` ENABLE KEYS */; UNLOCK TABLES; + +-- +-- Table structure for table `zones` +-- + +DROP TABLE IF EXISTS `zones`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `zones` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `depth` int(11) DEFAULT NULL, + `type_id` int(11) DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=53 DEFAULT CHARSET=latin1; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `zones` +-- + +LOCK TABLES `zones` WRITE; +/*!40000 ALTER TABLE `zones` DISABLE KEYS */; +INSERT INTO `zones` VALUES (1,1,4),(2,2,4),(3,3,3),(4,6,1),(5,7,1),(6,8,2),(7,9,1),(8,10,2),(9,11,1),(10,12,1),(11,13,2),(12,14,1),(13,15,2),(14,17,1),(15,18,1),(16,19,2),(17,20,1),(18,21,1),(19,22,2),(20,23,1),(21,24,2),(22,25,1),(23,26,2),(24,28,2),(25,29,2),(26,30,2),(27,31,2),(28,32,1),(29,33,1),(30,34,1),(31,35,2),(32,36,1),(33,37,1),(34,38,1),(35,41,2),(36,39,2),(37,40,1),(38,42,1),(39,43,1),(40,44,2),(41,45,2),(42,46,2),(43,47,2),(44,49,3),(45,50,3),(46,48,2),(47,51,3),(48,4,3),(49,5,2),(50,16,2),(51,27,2),(52,52,3); +/*!40000 ALTER TABLE `zones` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `zone_translations` +-- + +DROP TABLE IF EXISTS `zone_translations`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `zone_translations` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `zone_id` int(11) DEFAULT NULL, + `locale` varchar(255) DEFAULT NULL, + `label` varchar(255) DEFAULT NULL, + `plain_label` varchar(255) DEFAULT NULL, + `created_at` datetime DEFAULT NULL, + `updated_at` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `index_zone_translations_on_zone_id` (`zone_id`), + KEY `index_zone_translations_on_locale` (`locale`) +) ENGINE=InnoDB AUTO_INCREMENT=209 DEFAULT CHARSET=latin1; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `zone_translations` +-- + +LOCK TABLES `zone_translations` WRITE; +/*!40000 ALTER TABLE `zone_translations` DISABLE KEYS */; +INSERT INTO `zone_translations` VALUES (1,1,'en','Music','music','2013-01-27 06:10:45','2013-01-27 06:10:45'),(2,2,'en','Sound Effects','soundeffects','2013-01-27 06:10:45','2013-01-27 06:10:45'),(3,3,'en','Background','background','2013-01-27 06:10:45','2013-01-27 06:10:45'),(4,4,'en','Biology Effects','biologyeffects','2013-01-27 06:10:45','2013-01-27 06:10:45'),(5,5,'en','Hind Biology','hindbiology','2013-01-27 06:10:45','2013-01-27 06:10:45'),(6,6,'en','Markings','markings','2013-01-27 06:10:45','2013-01-27 06:10:45'),(7,7,'en','Hind Disease','hinddisease','2013-01-27 06:10:45','2013-01-27 06:10:45'),(8,8,'en','Hind Cover','hindcover','2013-01-27 06:10:45','2013-01-27 06:10:45'),(9,9,'en','Hind Transient Biology','hindtransientbiology','2013-01-27 06:10:45','2013-01-27 06:10:45'),(10,10,'en','Hind Drippings','hinddrippings','2013-01-27 06:10:45','2013-01-27 06:10:45'),(11,11,'en','Backpack','backpack','2013-01-27 06:10:45','2013-01-27 06:10:45'),(12,12,'en','Wings Transient Biology','wingstransientbiology','2013-01-27 06:10:45','2013-01-27 06:10:45'),(13,13,'en','Wings','wings','2013-01-27 06:10:45','2013-01-27 06:10:45'),(14,14,'en','Hair Back','hairback','2013-01-27 06:10:45','2013-01-27 06:10:45'),(15,15,'en','Body','body','2013-01-27 06:10:45','2013-01-27 06:10:45'),(16,16,'en','Markings','markings','2013-01-27 06:10:45','2013-01-27 06:10:45'),(17,17,'en','Body Disease','bodydisease','2013-01-27 06:10:45','2013-01-27 06:10:45'),(18,18,'en','Feet Transient Biology','feettransientbiology','2013-01-27 06:10:45','2013-01-27 06:10:45'),(19,19,'en','Shoes','shoes','2013-01-27 06:10:45','2013-01-27 06:10:45'),(20,20,'en','Lower-body Transient Biology','lowerbodytransientbiology','2013-01-27 06:10:45','2013-01-27 06:10:45'),(21,21,'en','Trousers','trousers','2013-01-27 06:10:45','2013-01-27 06:10:45'),(22,22,'en','Upper-body Transient Biology','upperbodytransientbiology','2013-01-27 06:10:45','2013-01-27 06:10:45'),(23,23,'en','Shirt/Dress','shirtdress','2013-01-27 06:10:45','2013-01-27 06:10:45'),(24,24,'en','Necklace','necklace','2013-01-27 06:10:45','2013-01-27 06:10:45'),(25,25,'en','Gloves','gloves','2013-01-27 06:10:45','2013-01-27 06:10:45'),(26,26,'en','Jacket','jacket','2013-01-27 06:10:45','2013-01-27 06:10:45'),(27,27,'en','Collar','collar','2013-01-27 06:10:45','2013-01-27 06:10:45'),(28,28,'en','Body Drippings','bodydrippings','2013-01-27 06:10:45','2013-01-27 06:10:45'),(29,29,'en','Ruff','ruff','2013-01-27 06:10:45','2013-01-27 06:10:45'),(30,30,'en','Head','head','2013-01-27 06:10:45','2013-01-27 06:10:45'),(31,31,'en','Markings','markings','2013-01-27 06:10:45','2013-01-27 06:10:45'),(32,32,'en','Head Disease','headdisease','2013-01-27 06:10:45','2013-01-27 06:10:45'),(33,33,'en','Eyes','eyes','2013-01-27 06:10:45','2013-01-27 06:10:45'),(34,34,'en','Mouth','mouth','2013-01-27 06:10:45','2013-01-27 06:10:45'),(35,35,'en','Glasses','glasses','2013-01-27 06:10:45','2013-01-27 06:10:45'),(36,36,'en','Earrings','earrings','2013-01-27 06:10:45','2013-01-27 06:10:45'),(37,37,'en','Hair Front','hairfront','2013-01-27 06:10:45','2013-01-27 06:10:45'),(38,38,'en','Head Transient Biology','headtransientbiology','2013-01-27 06:10:45','2013-01-27 06:10:45'),(39,39,'en','Head Drippings','headdrippings','2013-01-27 06:10:45','2013-01-27 06:10:45'),(40,40,'en','Hat','hat','2013-01-27 06:10:45','2013-01-27 06:10:45'),(41,41,'en','Earrings','earrings','2013-01-27 06:10:45','2013-01-27 06:10:45'),(42,42,'en','Right-hand Item','righthanditem','2013-01-27 06:10:45','2014-04-06 04:25:35'),(43,43,'en','Left-hand Item','lefthanditem','2013-01-27 06:10:45','2014-04-06 04:25:35'),(44,44,'en','Higher Foreground Item','higherforegrounditem','2013-01-27 06:10:45','2014-04-06 04:25:35'),(45,45,'en','Lower Foreground Item','lowerforegrounditem','2013-01-27 06:10:45','2014-04-06 04:25:35'),(46,46,'en','Static','static','2013-01-27 06:10:46','2013-01-27 06:10:46'),(47,47,'en','Thought Bubble','thoughtbubble','2013-01-27 06:10:46','2013-01-27 06:10:46'),(48,48,'en','Background Item','backgrounditem','2013-01-27 06:10:46','2014-04-06 04:25:35'),(49,49,'en','Right-hand Item','righthanditem','2013-01-27 06:10:46','2014-04-06 04:25:35'),(50,50,'en','Hat','hat','2013-01-27 06:10:46','2013-01-27 06:10:46'),(51,51,'en','Belt','belt','2013-01-27 06:10:46','2013-01-27 06:10:46'),(52,52,'en','Foreground','foreground','2013-01-27 06:10:46','2013-01-27 06:10:46'),(53,52,'pt','Frente','frente','2013-01-27 06:11:02','2013-01-27 06:11:02'),(54,47,'pt','Balão de Pensamento','balaodepensamento','2013-01-27 06:11:02','2013-01-27 06:11:02'),(55,45,'pt','Artigo Inferior Frontal','artigoinferiorfrontal','2013-01-27 06:11:02','2013-01-27 06:11:02'),(56,44,'pt','Artigo Superior Frontal','artigosuperiorfrontal','2013-01-27 06:11:02','2013-01-27 06:11:02'),(57,46,'pt','Static','static','2013-01-27 06:11:02','2013-01-27 06:11:02'),(58,43,'pt','Mão Esquerda','maoesquerda','2013-01-27 06:11:02','2013-01-27 06:11:02'),(59,42,'pt','Mão Direita','maodireita','2013-01-27 06:11:02','2013-01-27 06:11:02'),(60,41,'pt','Brincos','brincos','2013-01-27 06:11:02','2013-01-27 06:11:02'),(61,40,'pt','Chapéus','chapeus','2013-01-27 06:11:02','2013-01-27 06:11:02'),(62,39,'pt','Head Drippings','headdrippings','2013-01-27 06:11:02','2013-01-27 06:11:02'),(63,38,'pt','Head Transient Biology','headtransientbiology','2013-01-27 06:11:02','2013-01-27 06:11:02'),(64,35,'pt','Olhos','olhos','2013-01-27 06:11:02','2013-01-27 06:11:02'),(65,37,'pt','Pêlo Frontal','pelofrontal','2013-01-27 06:11:02','2013-01-27 06:11:02'),(66,36,'pt','Brincos','brincos','2013-01-27 06:11:02','2013-01-27 06:11:02'),(67,34,'pt','Mouth','mouth','2013-01-27 06:11:02','2013-01-27 06:11:02'),(68,33,'pt','Eyes','eyes','2013-01-27 06:11:02','2013-01-27 06:11:02'),(69,32,'pt','Head Disease','headdisease','2013-01-27 06:11:02','2013-01-27 06:11:02'),(70,31,'pt','Marcas','marcas','2013-01-27 06:11:02','2013-01-27 06:11:02'),(71,30,'pt','Head','head','2013-01-27 06:11:02','2013-01-27 06:11:02'),(72,29,'pt','Ruff','ruff','2013-01-27 06:11:02','2013-01-27 06:11:02'),(73,28,'pt','Body Drippings','bodydrippings','2013-01-27 06:11:02','2013-01-27 06:11:02'),(74,27,'pt','Pescoço','pescoco','2013-01-27 06:11:02','2013-01-27 06:11:02'),(75,26,'pt','Casacos','casacos','2013-01-27 06:11:02','2013-01-27 06:11:02'),(76,25,'pt','Luvas','luvas','2013-01-27 06:11:02','2013-01-27 06:11:02'),(77,24,'pt','Colares','colares','2013-01-27 06:11:02','2013-01-27 06:11:02'),(78,51,'pt','Cinto','cinto','2013-01-27 06:11:02','2013-01-27 06:11:02'),(79,23,'pt','Camisa/Vestido','camisavestido','2013-01-27 06:11:02','2013-01-27 06:11:02'),(80,22,'pt','Upper-body Transient Biology','upperbodytransientbiology','2013-01-27 06:11:02','2013-01-27 06:11:02'),(81,21,'pt','Calças','calcas','2013-01-27 06:11:02','2013-01-27 06:11:02'),(82,20,'pt','Lower-body Transient Biology','lowerbodytransientbiology','2013-01-27 06:11:02','2013-01-27 06:11:02'),(83,19,'pt','Sapatos','sapatos','2013-01-27 06:11:02','2013-01-27 06:11:02'),(84,18,'pt','Feet Transient Biology','feettransientbiology','2013-01-27 06:11:02','2013-01-27 06:11:02'),(85,17,'pt','Body Disease','bodydisease','2013-01-27 06:11:02','2013-01-27 06:11:02'),(86,16,'pt','Marcas','marcas','2013-01-27 06:11:02','2013-01-27 06:11:02'),(87,15,'pt','Body','body','2013-01-27 06:11:02','2013-01-27 06:11:02'),(88,14,'pt','Pêlo Traseiro','pelotraseiro','2013-01-27 06:11:02','2013-01-27 06:11:02'),(89,50,'pt','Chapéus','chapeus','2013-01-27 06:11:02','2013-01-27 06:11:02'),(90,13,'pt','Asas','asas','2013-01-27 06:11:02','2013-01-27 06:11:02'),(91,12,'pt','Wings Transient Biology','wingstransientbiology','2013-01-27 06:11:02','2013-01-27 06:11:02'),(92,11,'pt','Mochilas','mochilas','2013-01-27 06:11:02','2013-01-27 06:11:02'),(93,10,'pt','Hind Drippings','hinddrippings','2013-01-27 06:11:02','2013-01-27 06:11:02'),(94,9,'pt','Hind Transient Biology','hindtransientbiology','2013-01-27 06:11:02','2013-01-27 06:11:02'),(95,8,'pt','Adornos Traseiros','adornostraseiros','2013-01-27 06:11:02','2013-01-27 06:11:02'),(96,7,'pt','Hind Disease','hinddisease','2013-01-27 06:11:02','2013-01-27 06:11:02'),(97,6,'pt','Marcas','marcas','2013-01-27 06:11:02','2013-01-27 06:11:02'),(98,5,'pt','Hind Biology','hindbiology','2013-01-27 06:11:02','2013-01-27 06:11:02'),(99,4,'pt','Biology Effects','biologyeffects','2013-01-27 06:11:02','2013-01-27 06:11:02'),(100,49,'pt','Mão Direita','maodireita','2013-01-27 06:11:02','2013-01-27 06:11:02'),(101,48,'pt','Artigos de Fundo','artigosdefundo','2013-01-27 06:11:02','2013-01-27 06:11:02'),(102,3,'pt','Fundos de Imagem','fundosdeimagem','2013-01-27 06:11:02','2013-01-27 06:11:02'),(103,2,'pt','Efeitos Sonoros','efeitossonoros','2013-01-27 06:11:02','2013-01-27 06:11:02'),(104,1,'pt','Músicas','musicas','2013-01-27 06:11:02','2013-01-27 06:11:02'),(105,52,'en-MEEP','Foreground','foreground','2013-01-28 02:46:27','2013-01-28 02:46:27'),(106,47,'en-MEEP','Thought Bubble','thoughtbubble','2013-01-28 02:46:27','2013-01-28 02:46:27'),(107,45,'en-MEEP','Lower Foreground Item','lowerforegrounditem','2013-01-28 02:46:27','2013-01-28 02:46:27'),(108,44,'en-MEEP','Higher Foreground Item','higherforegrounditem','2013-01-28 02:46:27','2013-01-28 02:46:27'),(109,46,'en-MEEP','Static','static','2013-01-28 02:46:27','2013-01-28 02:46:27'),(110,43,'en-MEEP','Left-hand Item','lefthanditem','2013-01-28 02:46:27','2013-01-28 02:46:27'),(111,42,'en-MEEP','Right-hand Item','righthanditem','2013-01-28 02:46:27','2013-01-28 02:46:27'),(112,41,'en-MEEP','Earrings','earrings','2013-01-28 02:46:27','2013-01-28 02:46:27'),(113,40,'en-MEEP','Hat','hat','2013-01-28 02:46:27','2013-01-28 02:46:27'),(114,39,'en-MEEP','Head Drippings','headdrippings','2013-01-28 02:46:27','2013-01-28 02:46:27'),(115,38,'en-MEEP','Head Transient Biology','headtransientbiology','2013-01-28 02:46:27','2013-01-28 02:46:27'),(116,35,'en-MEEP','Glasses','glasses','2013-01-28 02:46:27','2013-01-28 02:46:27'),(117,37,'en-MEEP','Hair Front','hairfront','2013-01-28 02:46:27','2013-01-28 02:46:27'),(118,36,'en-MEEP','Earrings','earrings','2013-01-28 02:46:27','2013-01-28 02:46:27'),(119,34,'en-MEEP','Mouth','mouth','2013-01-28 02:46:27','2013-01-28 02:46:27'),(120,33,'en-MEEP','Eyes','eyes','2013-01-28 02:46:27','2013-01-28 02:46:27'),(121,32,'en-MEEP','Head Disease','headdisease','2013-01-28 02:46:27','2013-01-28 02:46:27'),(122,31,'en-MEEP','Markings','markings','2013-01-28 02:46:27','2013-01-28 02:46:27'),(123,30,'en-MEEP','Head','head','2013-01-28 02:46:27','2013-01-28 02:46:27'),(124,29,'en-MEEP','Ruff','ruff','2013-01-28 02:46:27','2013-01-28 02:46:27'),(125,28,'en-MEEP','Body Drippings','bodydrippings','2013-01-28 02:46:27','2013-01-28 02:46:27'),(126,27,'en-MEEP','Collar','collar','2013-01-28 02:46:27','2013-01-28 02:46:27'),(127,26,'en-MEEP','Jacket','jacket','2013-01-28 02:46:27','2013-01-28 02:46:27'),(128,25,'en-MEEP','Gloves','gloves','2013-01-28 02:46:27','2013-01-28 02:46:27'),(129,24,'en-MEEP','Necklace','necklace','2013-01-28 02:46:27','2013-01-28 02:46:27'),(130,51,'en-MEEP','Belt','belt','2013-01-28 02:46:27','2013-01-28 02:46:27'),(131,23,'en-MEEP','Shirt/Dress','shirtdress','2013-01-28 02:46:27','2013-01-28 02:46:27'),(132,22,'en-MEEP','Upper-body Transient Biology','upperbodytransientbiology','2013-01-28 02:46:27','2013-01-28 02:46:27'),(133,21,'en-MEEP','Trousers','trousers','2013-01-28 02:46:27','2013-01-28 02:46:27'),(134,20,'en-MEEP','Lower-body Transient Biology','lowerbodytransientbiology','2013-01-28 02:46:27','2013-01-28 02:46:27'),(135,19,'en-MEEP','Shoes','shoes','2013-01-28 02:46:27','2013-01-28 02:46:27'),(136,18,'en-MEEP','Feet Transient Biology','feettransientbiology','2013-01-28 02:46:28','2013-01-28 02:46:28'),(137,17,'en-MEEP','Body Disease','bodydisease','2013-01-28 02:46:28','2013-01-28 02:46:28'),(138,16,'en-MEEP','Markings','markings','2013-01-28 02:46:28','2013-01-28 02:46:28'),(139,15,'en-MEEP','Body','body','2013-01-28 02:46:28','2013-01-28 02:46:28'),(140,14,'en-MEEP','Hair Back','hairback','2013-01-28 02:46:28','2013-01-28 02:46:28'),(141,50,'en-MEEP','Hat','hat','2013-01-28 02:46:28','2013-01-28 02:46:28'),(142,13,'en-MEEP','Wings','wings','2013-01-28 02:46:28','2013-01-28 02:46:28'),(143,12,'en-MEEP','Wings Transient Biology','wingstransientbiology','2013-01-28 02:46:28','2013-01-28 02:46:28'),(144,11,'en-MEEP','Backpack','backpack','2013-01-28 02:46:28','2013-01-28 02:46:28'),(145,10,'en-MEEP','Hind Drippings','hinddrippings','2013-01-28 02:46:28','2013-01-28 02:46:28'),(146,9,'en-MEEP','Hind Transient Biology','hindtransientbiology','2013-01-28 02:46:28','2013-01-28 02:46:28'),(147,8,'en-MEEP','Hind Cover','hindcover','2013-01-28 02:46:28','2013-01-28 02:46:28'),(148,7,'en-MEEP','Hind Disease','hinddisease','2013-01-28 02:46:28','2013-01-28 02:46:28'),(149,6,'en-MEEP','Markings','markings','2013-01-28 02:46:28','2013-01-28 02:46:28'),(150,5,'en-MEEP','Hind Biology','hindbiology','2013-01-28 02:46:28','2013-01-28 02:46:28'),(151,4,'en-MEEP','Biology Effects','biologyeffects','2013-01-28 02:46:28','2013-01-28 02:46:28'),(152,49,'en-MEEP','Right-hand Item','righthanditem','2013-01-28 02:46:28','2013-01-28 02:46:28'),(153,48,'en-MEEP','Background Item','backgrounditem','2013-01-28 02:46:28','2013-01-28 02:46:28'),(154,3,'en-MEEP','Background','background','2013-01-28 02:46:28','2013-01-28 02:46:28'),(155,2,'en-MEEP','Sound Effects','soundeffects','2013-01-28 02:46:28','2013-01-28 02:46:28'),(156,1,'en-MEEP','Music','music','2013-01-28 02:46:28','2013-01-28 02:46:28'),(157,52,'es','Primer plano ','primerplano','2013-02-12 05:32:05','2013-02-12 05:32:05'),(158,47,'es','Burbujas de ideas','burbujasdeideas','2013-02-12 05:32:05','2013-02-12 05:32:05'),(159,45,'es','Objetos de primer plano bajo','objetosdeprimerplanobajo','2013-02-12 05:32:05','2013-02-12 05:32:05'),(160,44,'es','Objetos en primer plano alto ','objetosenprimerplanoalto','2013-02-12 05:32:05','2013-02-12 05:32:05'),(161,46,'es','Static','static','2013-02-12 05:32:05','2013-02-12 05:32:05'),(162,43,'es','Objetos para la mano izquierda','objetosparalamanoizquierda','2013-02-12 05:32:05','2013-02-12 05:32:05'),(163,42,'es','Objeto para la mano derecha','objetoparalamanoderecha','2013-02-12 05:32:05','2013-02-12 05:32:05'),(164,41,'es','Aretes','aretes','2013-02-12 05:32:05','2013-02-12 05:32:05'),(165,40,'es','Sombrero','sombrero','2013-02-12 05:32:05','2013-02-12 05:32:05'),(166,39,'es','Head Drippings','headdrippings','2013-02-12 05:32:05','2013-02-12 05:32:05'),(167,38,'es','Head Transient Biology','headtransientbiology','2013-02-12 05:32:05','2013-02-12 05:32:05'),(168,35,'es','Lentes','lentes','2013-02-12 05:32:05','2013-02-12 05:32:05'),(169,37,'es','Pelo de enfrente','pelodeenfrente','2013-02-12 05:32:05','2013-02-12 05:32:05'),(170,36,'es','Aretes','aretes','2013-02-12 05:32:05','2013-02-12 05:32:05'),(171,34,'es','Mouth','mouth','2013-02-12 05:32:05','2013-02-12 05:32:05'),(172,33,'es','Eyes','eyes','2013-02-12 05:32:05','2013-02-12 05:32:05'),(173,32,'es','Head Disease','headdisease','2013-02-12 05:32:05','2013-02-12 05:32:05'),(174,31,'es','Marcas','marcas','2013-02-12 05:32:05','2013-02-12 05:32:05'),(175,30,'es','Head','head','2013-02-12 05:32:05','2013-02-12 05:32:05'),(176,29,'es','Ruff','ruff','2013-02-12 05:32:05','2013-02-12 05:32:05'),(177,28,'es','Body Drippings','bodydrippings','2013-02-12 05:32:05','2013-02-12 05:32:05'),(178,27,'es','Cuello','cuello','2013-02-12 05:32:05','2013-02-12 05:32:05'),(179,26,'es','Saco','saco','2013-02-12 05:32:05','2013-02-12 05:32:05'),(180,25,'es','Guantes','guantes','2013-02-12 05:32:05','2013-02-12 05:32:05'),(181,24,'es','Collar','collar','2013-02-12 05:32:05','2013-02-12 05:32:05'),(182,51,'es','Cinturones','cinturones','2013-02-12 05:32:05','2013-02-12 05:32:05'),(183,23,'es','Camisetas/Vestidos','camisetasvestidos','2013-02-12 05:32:05','2013-02-12 05:32:05'),(184,22,'es','Upper-body Transient Biology','upperbodytransientbiology','2013-02-12 05:32:05','2013-02-12 05:32:05'),(185,21,'es','Pantalones/Faldas','pantalonesfaldas','2013-02-12 05:32:05','2013-02-12 05:32:05'),(186,20,'es','Lower-body Transient Biology','lowerbodytransientbiology','2013-02-12 05:32:06','2013-02-12 05:32:06'),(187,19,'es','Zapatos','zapatos','2013-02-12 05:32:06','2013-02-12 05:32:06'),(188,18,'es','Feet Transient Biology','feettransientbiology','2013-02-12 05:32:06','2013-02-12 05:32:06'),(189,17,'es','Body Disease','bodydisease','2013-02-12 05:32:06','2013-02-12 05:32:06'),(190,16,'es','Marcas','marcas','2013-02-12 05:32:06','2013-02-12 05:32:06'),(191,15,'es','Body','body','2013-02-12 05:32:06','2013-02-12 05:32:06'),(192,14,'es','Pelo de atrás','pelodeatras','2013-02-12 05:32:06','2013-02-12 05:32:06'),(193,50,'es','Sombrero','sombrero','2013-02-12 05:32:06','2013-02-12 05:32:06'),(194,13,'es','Alas','alas','2013-02-12 05:32:06','2013-02-12 05:32:06'),(195,12,'es','Wings Transient Biology','wingstransientbiology','2013-02-12 05:32:06','2013-02-12 05:32:06'),(196,11,'es','Mochila','mochila','2013-02-12 05:32:06','2013-02-12 05:32:06'),(197,10,'es','Hind Drippings','hinddrippings','2013-02-12 05:32:06','2013-02-12 05:32:06'),(198,9,'es','Hind Transient Biology','hindtransientbiology','2013-02-12 05:32:06','2013-02-12 05:32:06'),(199,8,'es','Cubierta trasera','cubiertatrasera','2013-02-12 05:32:06','2013-02-12 05:32:06'),(200,7,'es','Hind Disease','hinddisease','2013-02-12 05:32:06','2013-02-12 05:32:06'),(201,6,'es','Marcas','marcas','2013-02-12 05:32:06','2013-02-12 05:32:06'),(202,5,'es','Hind Biology','hindbiology','2013-02-12 05:32:06','2013-02-12 05:32:06'),(203,4,'es','Biology Effects','biologyeffects','2013-02-12 05:32:06','2013-02-12 05:32:06'),(204,49,'es','Objeto para la mano derecha','objetoparalamanoderecha','2013-02-12 05:32:06','2013-02-12 05:32:06'),(205,48,'es','Objetos de fondo','objetosdefondo','2013-02-12 05:32:06','2013-02-12 05:32:06'),(206,3,'es','Fondo','fondo','2013-02-12 05:32:06','2013-02-12 05:32:06'),(207,2,'es','Efectos de sonido','efectosdesonido','2013-02-12 05:32:06','2013-02-12 05:32:06'),(208,1,'es','Música','musica','2013-02-12 05:32:06','2013-02-12 05:32:06'); +/*!40000 ALTER TABLE `zone_translations` ENABLE KEYS */; +UNLOCK TABLES; /*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; /*!40101 SET SQL_MODE=@OLD_SQL_MODE */; @@ -133,4 +189,4 @@ UNLOCK TABLES; /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; --- Dump completed on 2020-09-18 6:27:12 +-- Dump completed on 2020-10-06 6:59:56 diff --git a/scripts/setup-mysql-dev-schema.sql b/scripts/setup-mysql-dev-schema.sql index ea3fb0f..f663d01 100644 --- a/scripts/setup-mysql-dev-schema.sql +++ b/scripts/setup-mysql-dev-schema.sql @@ -41,7 +41,7 @@ CREATE TABLE `items` ( `modeling_status_hint` enum('done','glitchy') COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`id`), KEY `objects_last_spidered` (`last_spidered`) -) ENGINE=InnoDB AUTO_INCREMENT=81718 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -65,7 +65,113 @@ CREATE TABLE `item_translations` ( KEY `index_item_translations_on_locale` (`locale`), KEY `index_item_translations_name` (`name`), KEY `index_item_translations_on_item_id_and_locale` (`item_id`,`locale`) -) ENGINE=InnoDB AUTO_INCREMENT=215758 DEFAULT CHARSET=latin1; +) ENGINE=InnoDB DEFAULT CHARSET=latin1; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `modeling_logs` +-- + +DROP TABLE IF EXISTS `modeling_logs`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `modeling_logs` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + `log_json` text NOT NULL, + `pet_name` varchar(128) NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `parents_swf_assets` +-- + +DROP TABLE IF EXISTS `parents_swf_assets`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `parents_swf_assets` ( + `parent_id` mediumint(9) NOT NULL, + `swf_asset_id` mediumint(9) NOT NULL, + `id` int(11) NOT NULL AUTO_INCREMENT, + `parent_type` varchar(8) COLLATE utf8_unicode_ci NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `unique_parents_swf_assets` (`parent_id`,`swf_asset_id`), + KEY `parents_swf_assets_swf_asset_id` (`swf_asset_id`), + KEY `index_parents_swf_assets_on_parent_id_and_parent_type` (`parent_id`,`parent_type`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `pet_types` +-- + +DROP TABLE IF EXISTS `pet_types`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `pet_types` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `color_id` tinyint(4) NOT NULL, + `species_id` tinyint(4) NOT NULL, + `created_at` datetime NOT NULL, + `body_id` smallint(6) NOT NULL, + `image_hash` varchar(8) COLLATE utf8_unicode_ci DEFAULT NULL, + `basic_image_hash` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `pet_types_species_color` (`species_id`,`color_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `pet_states` +-- + +DROP TABLE IF EXISTS `pet_states`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `pet_states` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `pet_type_id` mediumint(9) NOT NULL, + `swf_asset_ids` text COLLATE utf8_unicode_ci NOT NULL, + `female` tinyint(1) DEFAULT NULL, + `mood_id` int(11) DEFAULT NULL, + `unconverted` tinyint(1) DEFAULT NULL, + `labeled` tinyint(1) NOT NULL DEFAULT '0', + `glitched` tinyint(1) NOT NULL DEFAULT '0', + `artist_neopets_username` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `pet_states_pet_type_id` (`pet_type_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `swf_assets` +-- + +DROP TABLE IF EXISTS `swf_assets`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `swf_assets` ( + `type` varchar(7) COLLATE utf8_unicode_ci NOT NULL, + `remote_id` mediumint(9) NOT NULL, + `url` mediumtext COLLATE utf8_unicode_ci NOT NULL, + `zone_id` tinyint(4) NOT NULL, + `zones_restrict` text COLLATE utf8_unicode_ci NOT NULL, + `created_at` datetime NOT NULL, + `body_id` smallint(6) NOT NULL, + `has_image` tinyint(1) NOT NULL DEFAULT '0', + `image_requested` tinyint(1) NOT NULL DEFAULT '0', + `reported_broken_at` datetime DEFAULT NULL, + `converted_at` datetime DEFAULT NULL, + `id` int(11) NOT NULL AUTO_INCREMENT, + `image_manual` tinyint(1) NOT NULL DEFAULT '0', + `manifest` text COLLATE utf8_unicode_ci, + PRIMARY KEY (`id`), + KEY `swf_assets_body_id_and_object_id` (`body_id`), + KEY `idx_swf_assets_zone_id` (`zone_id`), + KEY `swf_assets_type_and_id` (`type`,`remote_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; /*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; @@ -77,4 +183,4 @@ CREATE TABLE `item_translations` ( /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; --- Dump completed on 2020-09-18 6:27:15 +-- Dump completed on 2020-10-06 6:59:49 diff --git a/scripts/setup-mysql.sql b/scripts/setup-mysql.sql index 68d69f8..4a5c8dc 100644 --- a/scripts/setup-mysql.sql +++ b/scripts/setup-mysql.sql @@ -5,6 +5,7 @@ GRANT SELECT ON colors TO impress2020; GRANT SELECT ON color_translations TO impress2020; GRANT SELECT ON items TO impress2020; GRANT SELECT ON item_translations TO impress2020; +GRANT SELECT ON modeling_logs TO impress2020; GRANT SELECT ON parents_swf_assets TO impress2020; GRANT SELECT ON pet_types TO impress2020; GRANT SELECT ON pet_states TO impress2020; @@ -14,11 +15,14 @@ GRANT SELECT ON swf_assets TO impress2020; GRANT SELECT ON zones TO impress2020; GRANT SELECT ON zone_translations TO impress2020; --- Public data tables: write -GRANT UPDATE ON items TO impress2020; -GRANT DELETE ON parents_swf_assets TO impress2020; -GRANT UPDATE ON pet_states TO impress2020; -GRANT UPDATE ON swf_assets TO impress2020; +-- Public data tables: write. Used in modeling and support tools. +GRANT INSERT, UPDATE ON items TO impress2020; +GRANT INSERT, UPDATE ON item_translations TO impress2020; +GRANT INSERT, UPDATE, DELETE ON parents_swf_assets TO impress2020; +GRANT INSERT, UPDATE ON pet_types TO impress2020; +GRANT INSERT, UPDATE ON pet_states TO impress2020; +GRANT INSERT, UPDATE ON swf_assets TO impress2020; +GRANT INSERT ON modeling_logs TO impress2020; -- User data tables GRANT SELECT ON closet_hangers TO impress2020; diff --git a/src/app/components/SpeciesColorPicker.js b/src/app/components/SpeciesColorPicker.js index e5e517a..26c490e 100644 --- a/src/app/components/SpeciesColorPicker.js +++ b/src/app/components/SpeciesColorPicker.js @@ -171,10 +171,15 @@ function SpeciesColorPicker({ // think this matches users' mental hierarchy of species -> color: showing // supported colors for a species makes sense, but the other way around feels // confusing and restrictive.) + // + // Also, if a color is provided that wouldn't normally be visible, we still + // show it. This can happen when someone models a new species/color combo for + // the first time - the boxes will still be red as if it were invalid, but + // this still smooths out the experience a lot. let visibleColors = allColors; if (stateMustAlwaysBeValid && valids && speciesId) { visibleColors = visibleColors.filter( - (c) => getValidPoses(valids, speciesId, c.id).size > 0 + (c) => getValidPoses(valids, speciesId, c.id).size > 0 || c.id === colorId ); } diff --git a/src/server/db.js b/src/server/db.js index a981f87..5acdac0 100644 --- a/src/server/db.js +++ b/src/server/db.js @@ -1,18 +1,40 @@ const mysql = require("mysql2"); -let globalDb; +let globalDbs = new Map(); + +// We usually run against the production database, even in local testing, +// to easily test against real data. (Not a wise general practice, but fine +// for this low-stakes project and small dev team with mostly read-only +// operations!) +// +// But you can also specify `DB_ENV=development` to use a local database, +// which is especially helpful for end-to-end modeling testing. +const defaultOptions = + process.env["DB_ENV"] === "development" + ? { + host: "localhost", + user: "impress_2020_dev", + password: "impress_2020_dev", + database: "impress_2020_dev", + } + : { + host: "impress.openneo.net", + user: process.env["IMPRESS_MYSQL_USER"], + password: process.env["IMPRESS_MYSQL_PASSWORD"], + database: "openneo_impress", + }; async function connectToDb({ - host = "impress.openneo.net", - user = process.env["IMPRESS_MYSQL_USER"], - password = process.env["IMPRESS_MYSQL_PASSWORD"], - database = "openneo_impress", + host = defaultOptions.host, + user = defaultOptions.user, + password = defaultOptions.password, + database = defaultOptions.database, } = {}) { - if (globalDb) { - return globalDb; + if (globalDbs.has(host)) { + return globalDbs.get(host); } - globalDb = mysql + const db = mysql .createConnection({ host, user, @@ -24,7 +46,9 @@ async function connectToDb({ // for compatibility with Honeycomb's automatic tracing. .promise(); - return globalDb; + globalDbs.set(host, db); + + return db; } module.exports = connectToDb; diff --git a/src/server/index.js b/src/server/index.js index edfacc9..55c5f55 100644 --- a/src/server/index.js +++ b/src/server/index.js @@ -40,6 +40,7 @@ const schema = makeExecutableSchema( require("./types/Item"), require("./types/MutationsForSupport"), require("./types/Outfit"), + require("./types/Pet"), require("./types/PetAppearance"), require("./types/User"), require("./types/Zone"), diff --git a/src/server/loaders.js b/src/server/loaders.js index 5d713e0..bada992 100644 --- a/src/server/loaders.js +++ b/src/server/loaders.js @@ -324,7 +324,7 @@ const buildItemBodiesWithAppearanceDataLoader = (db) => return itemIds.map((itemId) => entities.filter((e) => e.itemId === itemId)); }); -const buildPetTypeLoader = (db) => +const buildPetTypeLoader = (db, loaders) => new DataLoader(async (petTypeIds) => { const qs = petTypeIds.map((_) => "?").join(","); const [rows, _] = await db.execute( @@ -334,6 +334,13 @@ const buildPetTypeLoader = (db) => const entities = rows.map(normalizeRow); + for (const petType of entities) { + loaders.petTypeBySpeciesAndColorLoader.prime( + { speciesId: petType.speciesId, colorId: petType.colorId }, + petType + ); + } + return petTypeIds.map((petTypeId) => entities.find((e) => e.id === petTypeId) ); @@ -385,6 +392,29 @@ const buildSwfAssetLoader = (db) => ); }); +const buildSwfAssetByRemoteIdLoader = (db) => + new DataLoader( + async (typeAndRemoteIdPairs) => { + const qs = typeAndRemoteIdPairs + .map((_) => "(type = ? AND remote_id = ?)") + .join(" OR "); + const values = typeAndRemoteIdPairs + .map(({ type, remoteId }) => [type, remoteId]) + .flat(); + const [rows, _] = await db.execute( + `SELECT * FROM swf_assets WHERE ${qs}`, + values + ); + + const entities = rows.map(normalizeRow); + + return typeAndRemoteIdPairs.map(({ type, remoteId }) => + entities.find((e) => e.type === type && e.remoteId === remoteId) + ); + }, + { cacheKeyFn: ({ type, remoteId }) => `${type},${remoteId}` } + ); + const buildItemSwfAssetLoader = (db, loaders) => new DataLoader( async (itemAndBodyPairs) => { @@ -554,6 +584,37 @@ const buildCanonicalPetStateForBodyLoader = (db, loaders) => ); }); +const buildPetStateByPetTypeAndAssetsLoader = (db, loaders) => + new DataLoader( + async (petTypeIdAndAssetIdsPairs) => { + const qs = petTypeIdAndAssetIdsPairs + .map((_) => "(pet_type_id = ? AND swf_asset_ids = ?)") + .join(" OR "); + const values = petTypeIdAndAssetIdsPairs + .map(({ petTypeId, swfAssetIds }) => [petTypeId, swfAssetIds]) + .flat(); + const [rows, _] = await db.execute( + `SELECT * FROM pet_states WHERE ${qs}`, + values + ); + + const entities = rows.map(normalizeRow); + + for (const petState of entities) { + loaders.petStateLoader.prime(petState.id, petState); + } + + return petTypeIdAndAssetIdsPairs.map(({ petTypeId, swfAssetIds }) => + entities.find( + (e) => e.petTypeId === petTypeId && e.swfAssetIds === swfAssetIds + ) + ); + }, + { + cacheKeyFn: ({ petTypeId, swfAssetIds }) => `${petTypeId}-${swfAssetIds}`, + } + ); + const buildUserLoader = (db) => new DataLoader(async (ids) => { const qs = ids.map((_) => "?").join(","); @@ -671,12 +732,13 @@ function buildLoaders(db) { loaders.itemBodiesWithAppearanceDataLoader = buildItemBodiesWithAppearanceDataLoader( db ); - loaders.petTypeLoader = buildPetTypeLoader(db); + loaders.petTypeLoader = buildPetTypeLoader(db, loaders); loaders.petTypeBySpeciesAndColorLoader = buildPetTypeBySpeciesAndColorLoader( db, loaders ); loaders.swfAssetLoader = buildSwfAssetLoader(db); + loaders.swfAssetByRemoteIdLoader = buildSwfAssetByRemoteIdLoader(db); loaders.itemSwfAssetLoader = buildItemSwfAssetLoader(db, loaders); loaders.petSwfAssetLoader = buildPetSwfAssetLoader(db, loaders); loaders.outfitLoader = buildOutfitLoader(db); @@ -692,6 +754,10 @@ function buildLoaders(db) { db, loaders ); + loaders.petStateByPetTypeAndAssetsLoader = buildPetStateByPetTypeAndAssetsLoader( + db, + loaders + ); loaders.speciesLoader = buildSpeciesLoader(db); loaders.speciesTranslationLoader = buildSpeciesTranslationLoader(db); loaders.userLoader = buildUserLoader(db); diff --git a/src/server/modeling.js b/src/server/modeling.js new file mode 100644 index 0000000..7ac117b --- /dev/null +++ b/src/server/modeling.js @@ -0,0 +1,476 @@ +/** + * saveModelingData takes data about a pet (generated by `loadCustomPetData` + * and `loadPetMetaData`), and a GQL-y context object with a `db` and some + * loaders; and updates the database to match. + * + * These days, most calls to this function are a no-op: we detect that the + * database already contains this data, and end up doing no writes. But when a + * pet contains data we haven't seen before, we write! + */ +async function saveModelingData(customPetData, petMetaData, context) { + const modelingLogs = []; + const addToModelingLogs = (entry) => { + console.log("[Modeling] " + JSON.stringify(entry, null, 4)); + modelingLogs.push(entry); + }; + context = { ...context, addToModelingLogs }; + + await Promise.all([ + savePetTypeAndStateModelingData(customPetData, petMetaData, context), + saveItemModelingData(customPetData, context), + saveSwfAssetModelingData(customPetData, context), + ]); + + if (modelingLogs.length > 0) { + const { db } = context; + await db.execute( + `INSERT INTO modeling_logs (log_json, pet_name) VALUES (?, ?)`, + [JSON.stringify(modelingLogs, null, 4), petMetaData.name] + ); + } +} + +async function savePetTypeAndStateModelingData( + customPetData, + petMetaData, + context +) { + const { + db, + petTypeBySpeciesAndColorLoader, + petStateByPetTypeAndAssetsLoader, + swfAssetByRemoteIdLoader, + addToModelingLogs, + } = context; + + const incomingPetType = { + colorId: String(customPetData.custom_pet.color_id), + speciesId: String(customPetData.custom_pet.species_id), + bodyId: String(customPetData.custom_pet.body_id), + // NOTE: I skip the image_hash stuff here... on Rails, we set a hash on + // creation, and may or may not bother to update it, I forget? But + // here I don't want to bother with an update. We could maybe do + // a merge function to make it on create only, but eh, I don't + // care enough ^_^` + }; + + await syncToDb(db, [incomingPetType], { + loader: petTypeBySpeciesAndColorLoader, + tableName: "pet_types", + buildLoaderKey: (row) => ({ + speciesId: row.speciesId, + colorId: row.colorId, + }), + buildUpdateCondition: (row) => [ + `species_id = ? AND color_id = ?`, + row.speciesId, + row.colorId, + ], + includeUpdatedAt: false, + addToModelingLogs, + }); + + // NOTE: This pet type should have been looked up when syncing pet type, so + // this should be cached. + const petType = await petTypeBySpeciesAndColorLoader.load({ + colorId: String(customPetData.custom_pet.color_id), + speciesId: String(customPetData.custom_pet.species_id), + }); + const biologyAssets = Object.values(customPetData.custom_pet.biology_by_zone); + const incomingPetState = { + petTypeId: petType.id, + swfAssetIds: biologyAssets + .map((row) => row.part_id) + .sort((a, b) => Number(a) - Number(b)) + .join(","), + female: petMetaData.gender === 2 ? 1 : 0, // sorry for this column name :/ + moodId: String(petMetaData.mood), + unconverted: biologyAssets.length === 1 ? 1 : 0, + labeled: 1, + }; + + await syncToDb(db, [incomingPetState], { + loader: petStateByPetTypeAndAssetsLoader, + tableName: "pet_states", + buildLoaderKey: (row) => ({ + petTypeId: row.petTypeId, + swfAssetIds: row.swfAssetIds, + }), + buildUpdateCondition: (row) => [ + `pet_type_id = ? AND swf_asset_ids = ?`, + row.petTypeId, + row.swfAssetIds, + ], + includeCreatedAt: false, + includeUpdatedAt: false, + // For pet states, syncing assets is easy: a new set of assets counts as a + // new state, so, whatever! Just insert the relationships when inserting + // the pet state, and ignore them any other time. + afterInsert: async () => { + // We need to load from the db to get the actual inserted IDs. Not lovely + // for perf, but this is a real new-data model, so that's fine! + let [petState, swfAssets] = await Promise.all([ + petStateByPetTypeAndAssetsLoader.load({ + petTypeId: incomingPetState.petTypeId, + swfAssetIds: incomingPetState.swfAssetIds, + }), + swfAssetByRemoteIdLoader.loadMany( + biologyAssets.map((asset) => ({ + type: "biology", + remoteId: String(asset.part_id), + })) + ), + ]); + swfAssets = swfAssets.filter((sa) => sa != null); + if (swfAssets.length === 0) { + throw new Error(`pet state ${petState.id} has no saved assets?`); + } + + const relationshipInserts = swfAssets.map((sa) => ({ + parentType: "PetState", + parentId: petState.id, + swfAssetId: sa.id, + })); + + const qs = swfAssets.map((_) => `(?, ?, ?)`).join(", "); + const values = relationshipInserts + .map(({ parentType, parentId, swfAssetId }) => [ + parentType, + parentId, + swfAssetId, + ]) + .flat(); + await db.execute( + `INSERT INTO parents_swf_assets (parent_type, parent_id, swf_asset_id) + VALUES ${qs};`, + values + ); + + addToModelingLogs({ + tableName: "parents_swf_assets", + inserts: relationshipInserts, + updates: [], + }); + }, + addToModelingLogs, + }); +} + +async function saveItemModelingData(customPetData, context) { + const { db, itemLoader, itemTranslationLoader, addToModelingLogs } = context; + + const objectInfos = Object.values(customPetData.object_info_registry); + const incomingItems = objectInfos.map((objectInfo) => ({ + id: String(objectInfo.obj_info_id), + zonesRestrict: objectInfo.zones_restrict, + thumbnailUrl: objectInfo.thumbnail_url, + category: objectInfo.category, + type: objectInfo.type, + rarityIndex: objectInfo.rarity_index, + price: objectInfo.price, + weightLbs: objectInfo.weight_lbs, + })); + const incomingItemTranslations = objectInfos.map((objectInfo) => ({ + itemId: String(objectInfo.obj_info_id), + locale: "en", + name: objectInfo.name, + description: objectInfo.description, + rarity: objectInfo.rarity, + })); + + await Promise.all([ + syncToDb(db, incomingItems, { + loader: itemLoader, + tableName: "items", + buildLoaderKey: (row) => row.id, + buildUpdateCondition: (row) => [`id = ?`, row.id], + addToModelingLogs, + }), + syncToDb(db, incomingItemTranslations, { + loader: itemTranslationLoader, + tableName: "item_translations", + buildLoaderKey: (row) => row.itemId, + buildUpdateCondition: (row) => [ + `item_id = ? AND locale = "en"`, + row.itemId, + ], + addToModelingLogs, + }), + ]); +} + +async function saveSwfAssetModelingData(customPetData, context) { + const { db, swfAssetByRemoteIdLoader, addToModelingLogs } = context; + + const objectAssets = Object.values(customPetData.object_asset_registry); + const incomingItemSwfAssets = objectAssets.map((objectAsset) => ({ + type: "object", + remoteId: String(objectAsset.asset_id), + url: objectAsset.asset_url, + zoneId: String(objectAsset.zone_id), + zonesRestrict: "", + bodyId: (currentBodyId) => { + const incomingBodyId = String(customPetData.custom_pet.body_id); + + if (currentBodyId == null) { + // If this is a new asset, use the incoming body ID. This might not be + // totally true, the real ID might be 0, but we're conservative to + // start and will update it to 0 if we see a contradiction later! + // + // NOTE: There's an explicitly_body_specific column on Item. We don't + // need to consider it here, because it's specifically to + // override the heuristics in the old app that sometimes set + // bodyId=0 for incoming items depending on their zone. We don't + // do that here! + return incomingBodyId; + } else if (currentBodyId === "0") { + // If this is already an all-bodies asset, keep it that way. + return "0"; + } else if (currentBodyId !== incomingBodyId) { + // If this isn't an all-bodies asset yet, but we've now seen it on two + // different items, then make it an all-bodies asset! + return "0"; + } else { + // Okay, the row already exists, and its body ID matches this one. + // No change! + return currentBodyId; + } + }, + })); + + const biologyAssets = Object.values(customPetData.custom_pet.biology_by_zone); + const incomingPetSwfAssets = biologyAssets.map((biologyAsset) => ({ + type: "biology", + remoteId: String(biologyAsset.part_id), + url: biologyAsset.asset_url, + zoneId: String(biologyAsset.zone_id), + zonesRestrict: biologyAsset.zones_restrict, + bodyId: "0", + })); + + const incomingSwfAssets = [...incomingItemSwfAssets, ...incomingPetSwfAssets]; + + // Build a map from asset ID to item ID. We'll use this later to build the + // new parents_swf_assets rows. + const assetIdToItemIdMap = new Map(); + const objectInfos = Object.values(customPetData.object_info_registry); + for (const objectInfo of objectInfos) { + const itemId = String(objectInfo.obj_info_id); + const assetIds = Object.values(objectInfo.assets_by_zone).map(String); + for (const assetId of assetIds) { + assetIdToItemIdMap.set(assetId, itemId); + } + } + + syncToDb(db, incomingSwfAssets, { + loader: swfAssetByRemoteIdLoader, + tableName: "swf_assets", + buildLoaderKey: (row) => ({ type: row.type, remoteId: row.remoteId }), + buildUpdateCondition: (row) => [ + `type = ? AND remote_id = ?`, + row.type, + row.remoteId, + ], + includeUpdatedAt: false, + afterInsert: async (inserts) => { + // After inserting the assets, insert corresponding rows in + // parents_swf_assets for item assets, to mark the asset as belonging to + // the item. (We do this separately for pet states, so that we can get + // the pet state ID first.) + const itemAssetInserts = inserts.filter((i) => i.type === "object"); + if (itemAssetInserts.length === 0) { + return; + } + + const relationshipInserts = itemAssetInserts.map(({ remoteId }) => ({ + parentType: "Item", + parentId: assetIdToItemIdMap.get(remoteId), + remoteId, + })); + + const qs = itemAssetInserts + .map( + (_) => + // A bit cheesy: we use a subquery here to insert _our_ ID for the + // asset, despite only having remote_id available here. This saves + // us from another round-trip to SELECT the inserted IDs. + `(?, ?, ` + + `(SELECT id FROM swf_assets WHERE type = "object" AND remote_id = ?))` + ) + .join(", "); + const values = relationshipInserts + .map(({ parentType, parentId, remoteId }) => [ + parentType, + parentId, + remoteId, + ]) + .flat(); + + await db.execute( + `INSERT INTO parents_swf_assets (parent_type, parent_id, swf_asset_id) + VALUES ${qs}`, + values + ); + + addToModelingLogs({ + tableName: "parents_swf_assets", + inserts: relationshipInserts, + updates: [], + }); + }, + addToModelingLogs, + }); +} + +/** + * Syncs the given data to the database: for each incoming row, if there's no + * matching row in the loader, we insert a new row; or, if there's a matching + * row in the loader but its data is different, we update it; or, if there's + * no change, we do nothing. + * + * Automatically sets the `createdAt` and `updatedAt` timestamps for inserted + * or updated rows. + * + * Will perform one call to the loader, and at most one INSERT, and at most one + * UPDATE, regardless of how many rows we're syncing. + */ +async function syncToDb( + db, + incomingRows, + { + loader, + tableName, + buildLoaderKey, + buildUpdateCondition, + includeCreatedAt = true, + includeUpdatedAt = true, + afterInsert = null, + addToModelingLogs, + } +) { + const loaderKeys = incomingRows.map(buildLoaderKey); + const currentRows = await loader.loadMany(loaderKeys); + + const inserts = []; + const updates = []; + for (const index in incomingRows) { + const incomingRow = incomingRows[index]; + const currentRow = currentRows[index]; + + // If there is no corresponding row in the database, prepare an insert. + // TODO: Should probably converge on whether not-found is null or an error + if (currentRow == null || currentRow instanceof Error) { + const insert = {}; + for (const key in incomingRow) { + let incomingValue = incomingRow[key]; + + // If you pass a function as a value, we treat it as a merge function: + // we'll pass it the current value, and you'll use it to determine and + // return the incoming value. In this case, the row doesn't exist yet, + // so the current value is `null`. + if (typeof incomingValue === "function") { + incomingValue = incomingValue(null); + } + + insert[key] = incomingValue; + } + + if (includeCreatedAt) { + insert.createdAt = new Date(); + } + if (includeUpdatedAt) { + insert.updatedAt = new Date(); + } + inserts.push(insert); + + // Remove this from the loader cache, so that loading again will fetch + // the inserted row. + loader.clear(buildLoaderKey(incomingRow)); + + continue; + } + + // If there's a row in the database, and some of the values don't match, + // prepare an update with the updated fields only. + const update = {}; + for (const key in incomingRow) { + const currentValue = currentRow[key]; + let incomingValue = incomingRow[key]; + + // If you pass a function as a value, we treat it as a merge function: + // we'll pass it the current value, and you'll use it to determine and + // return the incoming value. + if (typeof incomingValue === "function") { + incomingValue = incomingValue(currentValue); + } + + if (currentValue !== incomingValue) { + update[key] = incomingValue; + } + } + + if (Object.keys(update).length > 0) { + if (includeUpdatedAt) { + update.updatedAt = new Date(); + } + updates.push({ incomingRow, update }); + + // Remove this from the loader cache, so that loading again will fetch + // the updated row. + loader.clear(buildLoaderKey(incomingRow)); + } + } + + // Do a bulk insert of anything that needs added. + if (inserts.length > 0) { + // Get the column names from the first row, and convert them to + // underscore-case instead of camel-case. + const rowKeys = Object.keys(inserts[0]).sort(); + const columnNames = rowKeys.map((key) => + key.replace(/[A-Z]/g, (m) => "_" + m[0].toLowerCase()) + ); + const columnsStr = columnNames.join(", "); + const qs = columnNames.map((_) => "?").join(", "); + const rowQs = inserts.map((_) => "(" + qs + ")").join(", "); + const rowValues = inserts.map((row) => rowKeys.map((key) => row[key])); + await db.execute( + `INSERT INTO ${tableName} (${columnsStr}) VALUES ${rowQs};`, + rowValues.flat() + ); + if (afterInsert) { + await afterInsert(inserts); + } + } + + // Do parallel updates of anything that needs updated. + // NOTE: I feel like it's not possible to do bulk updates, even in a + // multi-statement mysql2 request? I might be wrong, but whatever; it's + // very uncommon, and any perf hit would be nbd. + const updatePromises = []; + for (const { incomingRow, update } of updates) { + const rowKeys = Object.keys(update).sort(); + const rowValues = rowKeys.map((k) => update[k]); + const columnNames = rowKeys.map((key) => + key.replace(/[A-Z]/g, (m) => "_" + m[0].toLowerCase()) + ); + const qs = columnNames.map((c) => `${c} = ?`).join(", "); + const [conditionQs, ...conditionValues] = buildUpdateCondition(incomingRow); + updatePromises.push( + db.execute( + `UPDATE ${tableName} SET ${qs} WHERE ${conditionQs} LIMIT 1;`, + [...rowValues, ...conditionValues] + ) + ); + } + await Promise.all(updatePromises); + + if (inserts.length > 0 || updates.length > 0) { + addToModelingLogs({ + tableName, + inserts, + updates, + }); + } +} + +module.exports = { saveModelingData }; diff --git a/src/server/query-tests/Pet.test.js b/src/server/query-tests/Pet.test.js index f33aac3..401e975 100644 --- a/src/server/query-tests/Pet.test.js +++ b/src/server/query-tests/Pet.test.js @@ -1,5 +1,11 @@ const gql = require("graphql-tag"); -const { query, getDbCalls, clearDbCalls, useTestDb } = require("./setup.js"); +const { + query, + getDbCalls, + clearDbCalls, + useTestDb, + connectToDb, +} = require("./setup.js"); describe("Pet", () => { it("looks up a pet", async () => { @@ -33,6 +39,79 @@ describe("Pet", () => { expect(res.data).toMatchSnapshot(); expect(getDbCalls()).toMatchInlineSnapshot(` Array [ + Array [ + "SELECT * FROM pet_types WHERE (species_id = ? AND color_id = ?)", + Array [ + "54", + "75", + ], + ], + Array [ + "SELECT * FROM items WHERE id IN (?,?,?,?,?,?,?,?)", + Array [ + "37229", + "37375", + "38911", + "38912", + "38913", + "43014", + "43397", + "48313", + ], + ], + Array [ + "SELECT * FROM item_translations WHERE item_id IN (?,?,?,?,?,?,?,?) AND locale = \\"en\\"", + Array [ + "37229", + "37375", + "38911", + "38912", + "38913", + "43014", + "43397", + "48313", + ], + ], + Array [ + "SELECT * FROM swf_assets WHERE (type = ? AND remote_id = ?) OR (type = ? AND remote_id = ?) OR (type = ? AND remote_id = ?) OR (type = ? AND remote_id = ?) OR (type = ? AND remote_id = ?) OR (type = ? AND remote_id = ?) OR (type = ? AND remote_id = ?) OR (type = ? AND remote_id = ?) OR (type = ? AND remote_id = ?) OR (type = ? AND remote_id = ?) OR (type = ? AND remote_id = ?) OR (type = ? AND remote_id = ?) OR (type = ? AND remote_id = ?) OR (type = ? AND remote_id = ?)", + Array [ + "object", + "6829", + "object", + "14855", + "object", + "14856", + "object", + "14857", + "object", + "36414", + "object", + "39646", + "object", + "51959", + "object", + "56478", + "biology", + "7942", + "biology", + "7941", + "biology", + "24008", + "biology", + "21060", + "biology", + "21057", + "biology", + "7946", + ], + ], + Array [ + "SELECT * FROM pet_states WHERE (pet_type_id = ? AND swf_asset_ids = ?)", + Array [ + "2", + "7941,7942,7946,21057,21060,24008", + ], + ], Array [ "SELECT * FROM species_translations WHERE species_id IN (?) AND locale = \\"en\\"", @@ -51,7 +130,7 @@ describe("Pet", () => { `); }); - it("models new item data", async () => { + it("models new pet and item data", async () => { useTestDb(); const res = await query({ @@ -80,6 +159,20 @@ describe("Pet", () => { const res2 = await query({ query: gql` query { + petAppearance(colorId: "75", speciesId: "54", pose: SAD_MASC) { + id + pose + bodyId + + restrictedZones { + id + } + layers { + id + swfUrl + } + } + items( ids: [ "37229" @@ -99,6 +192,13 @@ describe("Pet", () => { rarityIndex isNc createdAt + + appearanceOn(colorId: "75", speciesId: "54") { + layers { + id + swfUrl + } + } } } `, @@ -106,35 +206,199 @@ describe("Pet", () => { expect(res2).toHaveNoErrors(); expect(res2.data).toMatchSnapshot(); - expect(getDbCalls()).toMatchInlineSnapshot(` - Array [ - Array [ - "SELECT * FROM item_translations WHERE item_id IN (?,?,?,?,?,?,?,?) AND locale = \\"en\\"", - Array [ - "37229", - "37375", - "38911", - "38912", - "38913", - "43014", - "43397", - "48313", - ], - ], - Array [ - "SELECT * FROM items WHERE id IN (?,?,?,?,?,?,?,?)", - Array [ - "37229", - "37375", - "38911", - "38912", - "38913", - "43014", - "43397", - "48313", - ], - ], - ] - `); + expect(getDbCalls()).toMatchSnapshot(); + + clearDbCalls(); + + // If we load the pet again, it should only make SELECT queries, not + // INSERT or UPDATE. + await query({ + query: gql` + query { + petOnNeopetsDotCom(petName: "roopal27") { + items { + id + } + } + } + `, + }); + + const dbCalls = getDbCalls(); + for (const [query, _] of dbCalls) { + expect(query).toMatch(/SELECT/); + expect(query).not.toMatch(/INSERT/); + expect(query).not.toMatch(/UPDATE/); + } + }); + + it("models updated item data", async () => { + useTestDb(); + + // First, write a fake version of the Jewelled Staff to the database. + // It's mostly the real data, except we changed rarity_index, + // thumbnail_url, translated name, and translated description. + const db = await connectToDb(); + await Promise.all([ + db.query( + `INSERT INTO items (id, zones_restrict, thumbnail_url, category, + type, rarity_index, price, weight_lbs) + VALUES (43397, "00000000000000000000000000000000000000000000000", + "http://example.com/favicon.ico", "Clothes", "Clothes", 101, + 0, 1);` + ), + db.query( + `INSERT INTO item_translations (item_id, locale, name, description, + rarity) + VALUES (43397, "en", "Bejewelled Staffo", + "This staff is really neat and good!", "Artifact")` + ), + ]); + + clearDbCalls(); + + // Then, load a pet wearing this. It should trigger an UPDATE for the item + // and its translation, and return the new names in the query. + const res = await query({ + query: gql` + query { + petOnNeopetsDotCom(petName: "roopal27") { + items { + id + name + description + thumbnailUrl + rarityIndex + } + } + } + `, + }); + + expect(res).toHaveNoErrors(); + const itemData = res.data.petOnNeopetsDotCom.items.find( + (item) => item.id === "43397" + ); + expect(itemData).toEqual({ + id: "43397", + name: "Jewelled Staff", + description: "This jewelled staff shines with a magical light.", + thumbnailUrl: "http://images.neopets.com/items/mall_staff_jewelled.gif", + rarityIndex: 500, + }); + expect(getDbCalls()).toMatchSnapshot(); + + clearDbCalls(); + + // Finally, load the item. It should have the updated values. + const res2 = await query({ + query: gql` + query { + item(id: "43397") { + id + name + description + thumbnailUrl + rarityIndex + } + } + `, + }); + + expect(res2).toHaveNoErrors(); + expect(res2.data.item).toEqual({ + id: "43397", + name: "Jewelled Staff", + description: "This jewelled staff shines with a magical light.", + thumbnailUrl: "http://images.neopets.com/items/mall_staff_jewelled.gif", + rarityIndex: 500, + }); + expect(getDbCalls()).toMatchSnapshot(); + }); + + it("sets bodyId=0 after seeing it on two body types", async () => { + useTestDb(); + + // First, write the Moon and Stars Background SWF to the database, but with + // the Standard Acara body ID set. + const db = await connectToDb(); + await db.query( + `INSERT INTO swf_assets (type, remote_id, url, zone_id, zones_restrict, + created_at, body_id) + VALUES ("object", 6829, "http://images.neopets.com/cp/items/swf/000/000/006/6829_1707e50385.swf", + 3, "", CURRENT_TIMESTAMP(), 93);` + ); + + clearDbCalls(); + + // Then, model a Zafara wearing it. + await query({ + query: gql` + query { + petOnNeopetsDotCom(petName: "roopal27") { + id + } + } + `, + }); + + expect(getDbCalls()).toMatchSnapshot("db"); + + // The body ID should be 0 now. + const [rows, _] = await db.query( + `SELECT body_id FROM swf_assets + WHERE type = "object" AND remote_id = 6829;` + ); + expect(rows[0].body_id).toEqual(0); + }); + + it("models unconverted pets", async () => { + useTestDb(); + + // First, model an unconverted pet, and check its pose and layers. + const res = await query({ + query: gql` + query { + petOnNeopetsDotCom(petName: "Marishka82") { + pose + petAppearance { + id + pose + layers { + id + } + } + } + } + `, + }); + expect(res).toHaveNoErrors(); + + const modeledPet = res.data.petOnNeopetsDotCom; + expect(modeledPet.pose).toEqual("UNCONVERTED"); + expect(modeledPet.petAppearance.pose).toEqual("UNCONVERTED"); + expect(modeledPet.petAppearance.layers).toHaveLength(1); + + // Then, request the corresponding appearance fresh from the db, and + // confirm we get the same back as when we modeled the pet. + const res2 = await query({ + query: gql` + query { + petAppearance(speciesId: "31", colorId: "36", pose: UNCONVERTED) { + id + layers { + id + } + } + } + `, + }); + expect(res2).toHaveNoErrors(); + + const petAppearance = res2.data.petAppearance; + expect(petAppearance.id).toEqual(modeledPet.petAppearance.id); + expect(petAppearance.layers.map((l) => l.id)).toEqual( + modeledPet.petAppearance.layers.map((l) => l.id) + ); }); }); diff --git a/src/server/query-tests/__snapshots__/Pet.test.js.snap b/src/server/query-tests/__snapshots__/Pet.test.js.snap index fdc50c8..bcb3f36 100644 --- a/src/server/query-tests/__snapshots__/Pet.test.js.snap +++ b/src/server/query-tests/__snapshots__/Pet.test.js.snap @@ -82,7 +82,7 @@ Object { } `; -exports[`Pet models new item data 1`] = ` +exports[`Pet models new pet and item data 1`] = ` Object { "petOnNeopetsDotCom": Object { "items": Array [ @@ -155,8 +155,15 @@ Object { } `; -exports[`Pet models new item data 2`] = ` +exports[`Pet models new pet and item data 2`] = ` Array [ + Array [ + "SELECT * FROM pet_types WHERE (species_id = ? AND color_id = ?)", + Array [ + "54", + "75", + ], + ], Array [ "SELECT * FROM items WHERE id IN (?,?,?,?,?,?,?,?)", Array [ @@ -184,166 +191,414 @@ Array [ ], ], Array [ - "INSERT INTO items - ( - id, zones_restrict, thumbnail_url, category, type, rarity_index, - price, weight_lbs, created_at, updated_at - ) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?, ?, ?, ?); - ", + "SELECT * FROM swf_assets WHERE (type = ? AND remote_id = ?) OR (type = ? AND remote_id = ?) OR (type = ? AND remote_id = ?) OR (type = ? AND remote_id = ?) OR (type = ? AND remote_id = ?) OR (type = ? AND remote_id = ?) OR (type = ? AND remote_id = ?) OR (type = ? AND remote_id = ?) OR (type = ? AND remote_id = ?) OR (type = ? AND remote_id = ?) OR (type = ? AND remote_id = ?) OR (type = ? AND remote_id = ?) OR (type = ? AND remote_id = ?) OR (type = ? AND remote_id = ?)", Array [ + "object", + "6829", + "object", + "14855", + "object", + "14856", + "object", + "14857", + "object", + "36414", + "object", + "39646", + "object", + "51959", + "object", + "56478", + "biology", + "7942", + "biology", + "7941", + "biology", + "24008", + "biology", + "21060", + "biology", + "21057", + "biology", + "7946", + ], + ], + Array [ + "INSERT INTO pet_types (body_id, color_id, created_at, species_id) VALUES (?, ?, ?, ?);", + Array [ + "180", + "75", + 2020-01-01T00:00:00.000Z, + "54", + ], + ], + Array [ + "INSERT INTO items (category, created_at, id, price, rarity_index, thumbnail_url, type, updated_at, weight_lbs, zones_restrict) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?, ?, ?, ?);", + Array [ + "Gift", + 2020-01-01T00:00:00.000Z, "37229", - "0000000000000000000000000000000000000000000000", + 0, + 101, "http://images.neopets.com/items/gif_magicball_table.gif", "Gift", - "Gift", - 101, - 0, - 1, 2020-01-01T00:00:00.000Z, + 1, + "0000000000000000000000000000000000000000000000", + "Special", 2020-01-01T00:00:00.000Z, "37375", - "0000000000000000000000000000000000000000000000000000", - "http://images.neopets.com/items/bg_moonstars.gif", - "Special", - "Mystical Surroundings", - 75, 209, - 1, + 75, + "http://images.neopets.com/items/bg_moonstars.gif", + "Mystical Surroundings", 2020-01-01T00:00:00.000Z, + 1, + "0000000000000000000000000000000000000000000000000000", + "Clothes", 2020-01-01T00:00:00.000Z, "38911", - "0000000000000000000000000000000000001100000000000000", + 980, + 92, "http://images.neopets.com/items/clo_zafara_agent_hood.gif", "Clothes", - "Clothes", - 92, - 980, - 1, 2020-01-01T00:00:00.000Z, + 1, + "0000000000000000000000000000000000001100000000000000", + "Clothes", 2020-01-01T00:00:00.000Z, "38912", - "0000000000000000000101000000000000000000000000000000", + 1476, + 90, "http://images.neopets.com/items/clo_zafara_agent_robe.gif", "Clothes", - "Clothes", - 90, - 1476, - 1, 2020-01-01T00:00:00.000Z, + 1, + "0000000000000000000101000000000000000000000000000000", + "Clothes", 2020-01-01T00:00:00.000Z, "38913", - "0000000000000000000000000000000000000000000000000000", + 1177, + 88, "http://images.neopets.com/items/clo_zafara_agent_gloves.gif", "Clothes", - "Clothes", - 88, - 1177, - 1, 2020-01-01T00:00:00.000Z, + 1, + "0000000000000000000000000000000000000000000000000000", + "Toys", 2020-01-01T00:00:00.000Z, "43014", - "0000000000000000000000000000000000000000000000", - "http://images.neopets.com/items/toy_stringlight_illleaf.gif", - "Toys", - "Toy", - 80, 1033, - 1, + 80, + "http://images.neopets.com/items/toy_stringlight_illleaf.gif", + "Toy", 2020-01-01T00:00:00.000Z, + 1, + "0000000000000000000000000000000000000000000000", + "Clothes", 2020-01-01T00:00:00.000Z, "43397", - "0000000000000000000000000000000000000000000000", + 0, + 500, "http://images.neopets.com/items/mall_staff_jewelled.gif", "Clothes", - "Clothes", - 500, - 0, - 1, 2020-01-01T00:00:00.000Z, + 1, + "0000000000000000000000000000000000000000000000", + "Clothes", 2020-01-01T00:00:00.000Z, "48313", - "0000000000000000000000000000000000000000000000000000", + 0, + 101, "http://images.neopets.com/items/clo_altcuplogo_brooch.gif", "Clothes", - "Clothes", - 101, - 0, - 1, 2020-01-01T00:00:00.000Z, + 1, + "0000000000000000000000000000000000000000000000000000", + ], + ], + Array [ + "INSERT INTO item_translations (created_at, description, item_id, locale, name, rarity, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?);", + Array [ + 2020-01-01T00:00:00.000Z, + "What does this ball actually do?", + "37229", + "en", + "Magic Ball Table", + "Special", + 2020-01-01T00:00:00.000Z, + 2020-01-01T00:00:00.000Z, + "Dont forget to wish upon a star.", + "37375", + "en", + "Moon and Stars Background", + "Uncommon", + 2020-01-01T00:00:00.000Z, + 2020-01-01T00:00:00.000Z, + "Hide your face and hair so no one can recognise you.", + "38911", + "en", + "Zafara Agent Hood", + "Very Rare", + 2020-01-01T00:00:00.000Z, + 2020-01-01T00:00:00.000Z, + "This robe is great for being stealthy.", + "38912", + "en", + "Zafara Agent Robe", + "Very Rare", + 2020-01-01T00:00:00.000Z, + 2020-01-01T00:00:00.000Z, + "Dont leave any trace that you were there with these gloves.", + "38913", + "en", + "Zafara Agent Gloves", + "Rare", + 2020-01-01T00:00:00.000Z, + 2020-01-01T00:00:00.000Z, + "These leaves almost look magical with their gentle glow.", + "43014", + "en", + "Green Leaf String Lights", + "Uncommon", + 2020-01-01T00:00:00.000Z, + 2020-01-01T00:00:00.000Z, + "This jewelled staff shines with a magical light.", + "43397", + "en", + "Jewelled Staff", + "Artifact", + 2020-01-01T00:00:00.000Z, + 2020-01-01T00:00:00.000Z, + "Even the announcers of the Altador Cup celebrate. This was given out by the Advent Calendar in Y11.", + "48313", + "en", + "Altador Cup Brooch", + "Special", 2020-01-01T00:00:00.000Z, ], ], Array [ - "INSERT INTO item_translations - (item_id, locale, name, description, rarity, created_at, updated_at) - VALUES (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?);", + "INSERT INTO swf_assets (body_id, created_at, remote_id, type, url, zone_id, zones_restrict) VALUES (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?);", Array [ - "37229", - "en", - "Magic Ball Table", - "What does this ball actually do?", - "Special", + "180", 2020-01-01T00:00:00.000Z, + "6829", + "object", + "http://images.neopets.com/cp/items/swf/000/000/006/6829_1707e50385.swf", + "3", + "", + "180", 2020-01-01T00:00:00.000Z, + "14855", + "object", + "http://images.neopets.com/cp/items/swf/000/000/014/14855_215f367070.swf", + "25", + "", + "180", + 2020-01-01T00:00:00.000Z, + "14856", + "object", + "http://images.neopets.com/cp/items/swf/000/000/014/14856_46c1b32797.swf", + "26", + "", + "180", + 2020-01-01T00:00:00.000Z, + "14857", + "object", + "http://images.neopets.com/cp/items/swf/000/000/014/14857_d43380ef66.swf", + "40", + "", + "180", + 2020-01-01T00:00:00.000Z, + "36414", + "object", + "http://images.neopets.com/cp/items/swf/000/000/036/36414_1e2aaab4ad.swf", + "48", + "", + "180", + 2020-01-01T00:00:00.000Z, + "39646", + "object", + "http://images.neopets.com/cp/items/swf/000/000/039/39646_e129e22ada.swf", + "42", + "", + "180", + 2020-01-01T00:00:00.000Z, + "51959", + "object", + "http://images.neopets.com/cp/items/swf/000/000/051/51959_4439727c48.swf", + "45", + "", + "180", + 2020-01-01T00:00:00.000Z, + "56478", + "object", + "http://images.neopets.com/cp/items/swf/000/000/056/56478_eabc28e7c7.swf", + "27", + "", + "0", + 2020-01-01T00:00:00.000Z, + "7942", + "biology", + "http://images.neopets.com/cp/bio/swf/000/000/007/7942_2eab06fd7b.swf", + "5", + "0000000000000000000000000000000000000000000000000000", + "0", + 2020-01-01T00:00:00.000Z, + "7941", + "biology", + "http://images.neopets.com/cp/bio/swf/000/000/007/7941_2c4cc4b846.swf", + "15", + "0000000000000000000000000000000000000000000000000000", + "0", + 2020-01-01T00:00:00.000Z, + "24008", + "biology", + "http://images.neopets.com/cp/bio/swf/000/000/024/24008_a05fe9876a.swf", + "30", + "0000000000000000000000000000000000000000000000000000", + "0", + 2020-01-01T00:00:00.000Z, + "21060", + "biology", + "http://images.neopets.com/cp/bio/swf/000/000/021/21060_d77ba93b7b.swf", + "33", + "0000000000000000000000000000000000000000000000000000", + "0", + 2020-01-01T00:00:00.000Z, + "21057", + "biology", + "http://images.neopets.com/cp/bio/swf/000/000/021/21057_4550efbb2f.swf", + "34", + "0000000000000000000000000000000000000000000000000000", + "0", + 2020-01-01T00:00:00.000Z, + "7946", + "biology", + "http://images.neopets.com/cp/bio/swf/000/000/007/7946_0348dad587.swf", + "37", + "0000000000000000000000000000000000000000000000000000", + ], + ], + Array [ + "SELECT * FROM pet_types WHERE (species_id = ? AND color_id = ?)", + Array [ + "54", + "75", + ], + ], + Array [ + "INSERT INTO parents_swf_assets (parent_type, parent_id, swf_asset_id) + VALUES (?, ?, (SELECT id FROM swf_assets WHERE type = \\"object\\" AND remote_id = ?)), (?, ?, (SELECT id FROM swf_assets WHERE type = \\"object\\" AND remote_id = ?)), (?, ?, (SELECT id FROM swf_assets WHERE type = \\"object\\" AND remote_id = ?)), (?, ?, (SELECT id FROM swf_assets WHERE type = \\"object\\" AND remote_id = ?)), (?, ?, (SELECT id FROM swf_assets WHERE type = \\"object\\" AND remote_id = ?)), (?, ?, (SELECT id FROM swf_assets WHERE type = \\"object\\" AND remote_id = ?)), (?, ?, (SELECT id FROM swf_assets WHERE type = \\"object\\" AND remote_id = ?)), (?, ?, (SELECT id FROM swf_assets WHERE type = \\"object\\" AND remote_id = ?))", + Array [ + "Item", "37375", - "en", - "Moon and Stars Background", - "Dont forget to wish upon a star.", - "Uncommon", - 2020-01-01T00:00:00.000Z, - 2020-01-01T00:00:00.000Z, - "38911", - "en", - "Zafara Agent Hood", - "Hide your face and hair so no one can recognise you.", - "Very Rare", - 2020-01-01T00:00:00.000Z, - 2020-01-01T00:00:00.000Z, - "38912", - "en", - "Zafara Agent Robe", - "This robe is great for being stealthy.", - "Very Rare", - 2020-01-01T00:00:00.000Z, - 2020-01-01T00:00:00.000Z, + "6829", + "Item", "38913", - "en", - "Zafara Agent Gloves", - "Dont leave any trace that you were there with these gloves.", - "Rare", - 2020-01-01T00:00:00.000Z, - 2020-01-01T00:00:00.000Z, + "14855", + "Item", + "38912", + "14856", + "Item", + "38911", + "14857", + "Item", "43014", - "en", - "Green Leaf String Lights", - "These leaves almost look magical with their gentle glow.", - "Uncommon", - 2020-01-01T00:00:00.000Z, - 2020-01-01T00:00:00.000Z, + "36414", + "Item", "43397", - "en", - "Jewelled Staff", - "This jewelled staff shines with a magical light.", - "Artifact", - 2020-01-01T00:00:00.000Z, - 2020-01-01T00:00:00.000Z, + "39646", + "Item", + "37229", + "51959", + "Item", "48313", - "en", - "Altador Cup Brooch", - "Even the announcers of the Altador Cup celebrate. This was given out by the Advent Calendar in Y11.", - "Special", - 2020-01-01T00:00:00.000Z, - 2020-01-01T00:00:00.000Z, + "56478", + ], + ], + Array [ + "SELECT * FROM pet_states WHERE (pet_type_id = ? AND swf_asset_ids = ?)", + Array [ + "1", + "7941,7942,7946,21057,21060,24008", + ], + ], + Array [ + "INSERT INTO pet_states (female, labeled, mood_id, pet_type_id, swf_asset_ids, unconverted) VALUES (?, ?, ?, ?, ?, ?);", + Array [ + 0, + 1, + "2", + "1", + "7941,7942,7946,21057,21060,24008", + 0, + ], + ], + Array [ + "SELECT * FROM pet_states WHERE (pet_type_id = ? AND swf_asset_ids = ?)", + Array [ + "1", + "7941,7942,7946,21057,21060,24008", + ], + ], + Array [ + "SELECT * FROM swf_assets WHERE (type = ? AND remote_id = ?) OR (type = ? AND remote_id = ?) OR (type = ? AND remote_id = ?) OR (type = ? AND remote_id = ?) OR (type = ? AND remote_id = ?) OR (type = ? AND remote_id = ?)", + Array [ + "biology", + "7942", + "biology", + "7941", + "biology", + "24008", + "biology", + "21060", + "biology", + "21057", + "biology", + "7946", + ], + ], + Array [ + "INSERT INTO parents_swf_assets (parent_type, parent_id, swf_asset_id) + VALUES (?, ?, ?), (?, ?, ?), (?, ?, ?), (?, ?, ?), (?, ?, ?), (?, ?, ?);", + Array [ + "PetState", + "1", + "9", + "PetState", + "1", + "10", + "PetState", + "1", + "11", + "PetState", + "1", + "12", + "PetState", + "1", + "13", + "PetState", + "1", + "14", ], ], ] `; -exports[`Pet models new item data 3`] = ` +exports[`Pet models new pet and item data 3`] = ` Object { "items": Array [ Object { + "appearanceOn": Object { + "layers": Array [ + Object { + "id": "7", + "swfUrl": "http://images.neopets.com/cp/items/swf/000/000/051/51959_4439727c48.swf", + }, + ], + }, "createdAt": "2020-01-01T00:00:00.000Z", "description": "What does this ball actually do?", "id": "37229", @@ -353,6 +608,14 @@ Object { "thumbnailUrl": "http://images.neopets.com/items/gif_magicball_table.gif", }, Object { + "appearanceOn": Object { + "layers": Array [ + Object { + "id": "1", + "swfUrl": "http://images.neopets.com/cp/items/swf/000/000/006/6829_1707e50385.swf", + }, + ], + }, "createdAt": "2020-01-01T00:00:00.000Z", "description": "Dont forget to wish upon a star.", "id": "37375", @@ -362,6 +625,14 @@ Object { "thumbnailUrl": "http://images.neopets.com/items/bg_moonstars.gif", }, Object { + "appearanceOn": Object { + "layers": Array [ + Object { + "id": "4", + "swfUrl": "http://images.neopets.com/cp/items/swf/000/000/014/14857_d43380ef66.swf", + }, + ], + }, "createdAt": "2020-01-01T00:00:00.000Z", "description": "Hide your face and hair so no one can recognise you.", "id": "38911", @@ -371,6 +642,14 @@ Object { "thumbnailUrl": "http://images.neopets.com/items/clo_zafara_agent_hood.gif", }, Object { + "appearanceOn": Object { + "layers": Array [ + Object { + "id": "3", + "swfUrl": "http://images.neopets.com/cp/items/swf/000/000/014/14856_46c1b32797.swf", + }, + ], + }, "createdAt": "2020-01-01T00:00:00.000Z", "description": "This robe is great for being stealthy.", "id": "38912", @@ -380,6 +659,14 @@ Object { "thumbnailUrl": "http://images.neopets.com/items/clo_zafara_agent_robe.gif", }, Object { + "appearanceOn": Object { + "layers": Array [ + Object { + "id": "2", + "swfUrl": "http://images.neopets.com/cp/items/swf/000/000/014/14855_215f367070.swf", + }, + ], + }, "createdAt": "2020-01-01T00:00:00.000Z", "description": "Dont leave any trace that you were there with these gloves.", "id": "38913", @@ -389,6 +676,14 @@ Object { "thumbnailUrl": "http://images.neopets.com/items/clo_zafara_agent_gloves.gif", }, Object { + "appearanceOn": Object { + "layers": Array [ + Object { + "id": "5", + "swfUrl": "http://images.neopets.com/cp/items/swf/000/000/036/36414_1e2aaab4ad.swf", + }, + ], + }, "createdAt": "2020-01-01T00:00:00.000Z", "description": "These leaves almost look magical with their gentle glow.", "id": "43014", @@ -398,6 +693,14 @@ Object { "thumbnailUrl": "http://images.neopets.com/items/toy_stringlight_illleaf.gif", }, Object { + "appearanceOn": Object { + "layers": Array [ + Object { + "id": "6", + "swfUrl": "http://images.neopets.com/cp/items/swf/000/000/039/39646_e129e22ada.swf", + }, + ], + }, "createdAt": "2020-01-01T00:00:00.000Z", "description": "This jewelled staff shines with a magical light.", "id": "43397", @@ -407,6 +710,14 @@ Object { "thumbnailUrl": "http://images.neopets.com/items/mall_staff_jewelled.gif", }, Object { + "appearanceOn": Object { + "layers": Array [ + Object { + "id": "8", + "swfUrl": "http://images.neopets.com/cp/items/swf/000/000/056/56478_eabc28e7c7.swf", + }, + ], + }, "createdAt": "2020-01-01T00:00:00.000Z", "description": "Even the announcers of the Altador Cup celebrate. This was given out by the Advent Calendar in Y11.", "id": "48313", @@ -416,5 +727,1000 @@ Object { "thumbnailUrl": "http://images.neopets.com/items/clo_altcuplogo_brooch.gif", }, ], + "petAppearance": Object { + "bodyId": "180", + "id": "1", + "layers": Array [ + Object { + "id": "9", + "swfUrl": "http://images.neopets.com/cp/bio/swf/000/000/007/7942_2eab06fd7b.swf", + }, + Object { + "id": "10", + "swfUrl": "http://images.neopets.com/cp/bio/swf/000/000/007/7941_2c4cc4b846.swf", + }, + Object { + "id": "11", + "swfUrl": "http://images.neopets.com/cp/bio/swf/000/000/024/24008_a05fe9876a.swf", + }, + Object { + "id": "12", + "swfUrl": "http://images.neopets.com/cp/bio/swf/000/000/021/21060_d77ba93b7b.swf", + }, + Object { + "id": "13", + "swfUrl": "http://images.neopets.com/cp/bio/swf/000/000/021/21057_4550efbb2f.swf", + }, + Object { + "id": "14", + "swfUrl": "http://images.neopets.com/cp/bio/swf/000/000/007/7946_0348dad587.swf", + }, + ], + "pose": "SAD_MASC", + "restrictedZones": Array [], + }, } `; + +exports[`Pet models new pet and item data 4`] = ` +Array [ + Array [ + "SELECT * FROM pet_types WHERE (species_id = ? AND color_id = ?)", + Array [ + "54", + "75", + ], + ], + Array [ + "SELECT * FROM item_translations WHERE item_id IN (?,?,?,?,?,?,?,?) AND locale = \\"en\\"", + Array [ + "37229", + "37375", + "38911", + "38912", + "38913", + "43014", + "43397", + "48313", + ], + ], + Array [ + "SELECT * FROM items WHERE id IN (?,?,?,?,?,?,?,?)", + Array [ + "37229", + "37375", + "38911", + "38912", + "38913", + "43014", + "43397", + "48313", + ], + ], + Array [ + "SELECT * FROM pet_states + WHERE pet_type_id IN (?) + ORDER BY (mood_id IS NULL) ASC, mood_id ASC, female DESC, + unconverted DESC, glitched ASC, id DESC", + Array [ + "1", + ], + ], + Array [ + "SELECT sa.*, rel.parent_id FROM swf_assets sa + INNER JOIN parents_swf_assets rel ON + rel.parent_type = \\"Item\\" AND + rel.swf_asset_id = sa.id + WHERE (rel.parent_id = ? AND (sa.body_id = ? OR sa.body_id = 0)) OR (rel.parent_id = ? AND (sa.body_id = ? OR sa.body_id = 0)) OR (rel.parent_id = ? AND (sa.body_id = ? OR sa.body_id = 0)) OR (rel.parent_id = ? AND (sa.body_id = ? OR sa.body_id = 0)) OR (rel.parent_id = ? AND (sa.body_id = ? OR sa.body_id = 0)) OR (rel.parent_id = ? AND (sa.body_id = ? OR sa.body_id = 0)) OR (rel.parent_id = ? AND (sa.body_id = ? OR sa.body_id = 0)) OR (rel.parent_id = ? AND (sa.body_id = ? OR sa.body_id = 0))", + Array [ + "37229", + "180", + "37375", + "180", + "38911", + "180", + "38912", + "180", + "38913", + "180", + "43014", + "180", + "43397", + "180", + "48313", + "180", + ], + ], + Array [ + "SELECT sa.*, rel.parent_id FROM swf_assets sa + INNER JOIN parents_swf_assets rel ON + rel.parent_type = \\"PetState\\" AND + rel.swf_asset_id = sa.id + WHERE rel.parent_id IN (?)", + Array [ + "1", + ], + ], +] +`; + +exports[`Pet models updated item data 1`] = ` +Array [ + Array [ + "SELECT * FROM pet_types WHERE (species_id = ? AND color_id = ?)", + Array [ + "54", + "75", + ], + ], + Array [ + "SELECT * FROM items WHERE id IN (?,?,?,?,?,?,?,?)", + Array [ + "37229", + "37375", + "38911", + "38912", + "38913", + "43014", + "43397", + "48313", + ], + ], + Array [ + "SELECT * FROM item_translations WHERE item_id IN (?,?,?,?,?,?,?,?) AND locale = \\"en\\"", + Array [ + "37229", + "37375", + "38911", + "38912", + "38913", + "43014", + "43397", + "48313", + ], + ], + Array [ + "SELECT * FROM swf_assets WHERE (type = ? AND remote_id = ?) OR (type = ? AND remote_id = ?) OR (type = ? AND remote_id = ?) OR (type = ? AND remote_id = ?) OR (type = ? AND remote_id = ?) OR (type = ? AND remote_id = ?) OR (type = ? AND remote_id = ?) OR (type = ? AND remote_id = ?) OR (type = ? AND remote_id = ?) OR (type = ? AND remote_id = ?) OR (type = ? AND remote_id = ?) OR (type = ? AND remote_id = ?) OR (type = ? AND remote_id = ?) OR (type = ? AND remote_id = ?)", + Array [ + "object", + "6829", + "object", + "14855", + "object", + "14856", + "object", + "14857", + "object", + "36414", + "object", + "39646", + "object", + "51959", + "object", + "56478", + "biology", + "7942", + "biology", + "7941", + "biology", + "24008", + "biology", + "21060", + "biology", + "21057", + "biology", + "7946", + ], + ], + Array [ + "INSERT INTO pet_types (body_id, color_id, created_at, species_id) VALUES (?, ?, ?, ?);", + Array [ + "180", + "75", + 2020-01-01T00:00:00.000Z, + "54", + ], + ], + Array [ + "INSERT INTO items (category, created_at, id, price, rarity_index, thumbnail_url, type, updated_at, weight_lbs, zones_restrict) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?, ?, ?, ?);", + Array [ + "Gift", + 2020-01-01T00:00:00.000Z, + "37229", + 0, + 101, + "http://images.neopets.com/items/gif_magicball_table.gif", + "Gift", + 2020-01-01T00:00:00.000Z, + 1, + "0000000000000000000000000000000000000000000000", + "Special", + 2020-01-01T00:00:00.000Z, + "37375", + 209, + 75, + "http://images.neopets.com/items/bg_moonstars.gif", + "Mystical Surroundings", + 2020-01-01T00:00:00.000Z, + 1, + "0000000000000000000000000000000000000000000000000000", + "Clothes", + 2020-01-01T00:00:00.000Z, + "38911", + 980, + 92, + "http://images.neopets.com/items/clo_zafara_agent_hood.gif", + "Clothes", + 2020-01-01T00:00:00.000Z, + 1, + "0000000000000000000000000000000000001100000000000000", + "Clothes", + 2020-01-01T00:00:00.000Z, + "38912", + 1476, + 90, + "http://images.neopets.com/items/clo_zafara_agent_robe.gif", + "Clothes", + 2020-01-01T00:00:00.000Z, + 1, + "0000000000000000000101000000000000000000000000000000", + "Clothes", + 2020-01-01T00:00:00.000Z, + "38913", + 1177, + 88, + "http://images.neopets.com/items/clo_zafara_agent_gloves.gif", + "Clothes", + 2020-01-01T00:00:00.000Z, + 1, + "0000000000000000000000000000000000000000000000000000", + "Toys", + 2020-01-01T00:00:00.000Z, + "43014", + 1033, + 80, + "http://images.neopets.com/items/toy_stringlight_illleaf.gif", + "Toy", + 2020-01-01T00:00:00.000Z, + 1, + "0000000000000000000000000000000000000000000000", + "Clothes", + 2020-01-01T00:00:00.000Z, + "48313", + 0, + 101, + "http://images.neopets.com/items/clo_altcuplogo_brooch.gif", + "Clothes", + 2020-01-01T00:00:00.000Z, + 1, + "0000000000000000000000000000000000000000000000000000", + ], + ], + Array [ + "INSERT INTO item_translations (created_at, description, item_id, locale, name, rarity, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?);", + Array [ + 2020-01-01T00:00:00.000Z, + "What does this ball actually do?", + "37229", + "en", + "Magic Ball Table", + "Special", + 2020-01-01T00:00:00.000Z, + 2020-01-01T00:00:00.000Z, + "Dont forget to wish upon a star.", + "37375", + "en", + "Moon and Stars Background", + "Uncommon", + 2020-01-01T00:00:00.000Z, + 2020-01-01T00:00:00.000Z, + "Hide your face and hair so no one can recognise you.", + "38911", + "en", + "Zafara Agent Hood", + "Very Rare", + 2020-01-01T00:00:00.000Z, + 2020-01-01T00:00:00.000Z, + "This robe is great for being stealthy.", + "38912", + "en", + "Zafara Agent Robe", + "Very Rare", + 2020-01-01T00:00:00.000Z, + 2020-01-01T00:00:00.000Z, + "Dont leave any trace that you were there with these gloves.", + "38913", + "en", + "Zafara Agent Gloves", + "Rare", + 2020-01-01T00:00:00.000Z, + 2020-01-01T00:00:00.000Z, + "These leaves almost look magical with their gentle glow.", + "43014", + "en", + "Green Leaf String Lights", + "Uncommon", + 2020-01-01T00:00:00.000Z, + 2020-01-01T00:00:00.000Z, + "Even the announcers of the Altador Cup celebrate. This was given out by the Advent Calendar in Y11.", + "48313", + "en", + "Altador Cup Brooch", + "Special", + 2020-01-01T00:00:00.000Z, + ], + ], + Array [ + "INSERT INTO swf_assets (body_id, created_at, remote_id, type, url, zone_id, zones_restrict) VALUES (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?);", + Array [ + "180", + 2020-01-01T00:00:00.000Z, + "6829", + "object", + "http://images.neopets.com/cp/items/swf/000/000/006/6829_1707e50385.swf", + "3", + "", + "180", + 2020-01-01T00:00:00.000Z, + "14855", + "object", + "http://images.neopets.com/cp/items/swf/000/000/014/14855_215f367070.swf", + "25", + "", + "180", + 2020-01-01T00:00:00.000Z, + "14856", + "object", + "http://images.neopets.com/cp/items/swf/000/000/014/14856_46c1b32797.swf", + "26", + "", + "180", + 2020-01-01T00:00:00.000Z, + "14857", + "object", + "http://images.neopets.com/cp/items/swf/000/000/014/14857_d43380ef66.swf", + "40", + "", + "180", + 2020-01-01T00:00:00.000Z, + "36414", + "object", + "http://images.neopets.com/cp/items/swf/000/000/036/36414_1e2aaab4ad.swf", + "48", + "", + "180", + 2020-01-01T00:00:00.000Z, + "39646", + "object", + "http://images.neopets.com/cp/items/swf/000/000/039/39646_e129e22ada.swf", + "42", + "", + "180", + 2020-01-01T00:00:00.000Z, + "51959", + "object", + "http://images.neopets.com/cp/items/swf/000/000/051/51959_4439727c48.swf", + "45", + "", + "180", + 2020-01-01T00:00:00.000Z, + "56478", + "object", + "http://images.neopets.com/cp/items/swf/000/000/056/56478_eabc28e7c7.swf", + "27", + "", + "0", + 2020-01-01T00:00:00.000Z, + "7942", + "biology", + "http://images.neopets.com/cp/bio/swf/000/000/007/7942_2eab06fd7b.swf", + "5", + "0000000000000000000000000000000000000000000000000000", + "0", + 2020-01-01T00:00:00.000Z, + "7941", + "biology", + "http://images.neopets.com/cp/bio/swf/000/000/007/7941_2c4cc4b846.swf", + "15", + "0000000000000000000000000000000000000000000000000000", + "0", + 2020-01-01T00:00:00.000Z, + "24008", + "biology", + "http://images.neopets.com/cp/bio/swf/000/000/024/24008_a05fe9876a.swf", + "30", + "0000000000000000000000000000000000000000000000000000", + "0", + 2020-01-01T00:00:00.000Z, + "21060", + "biology", + "http://images.neopets.com/cp/bio/swf/000/000/021/21060_d77ba93b7b.swf", + "33", + "0000000000000000000000000000000000000000000000000000", + "0", + 2020-01-01T00:00:00.000Z, + "21057", + "biology", + "http://images.neopets.com/cp/bio/swf/000/000/021/21057_4550efbb2f.swf", + "34", + "0000000000000000000000000000000000000000000000000000", + "0", + 2020-01-01T00:00:00.000Z, + "7946", + "biology", + "http://images.neopets.com/cp/bio/swf/000/000/007/7946_0348dad587.swf", + "37", + "0000000000000000000000000000000000000000000000000000", + ], + ], + Array [ + "SELECT * FROM pet_types WHERE (species_id = ? AND color_id = ?)", + Array [ + "54", + "75", + ], + ], + Array [ + "UPDATE items SET rarity_index = ?, thumbnail_url = ?, updated_at = ?, zones_restrict = ? WHERE id = ? LIMIT 1;", + Array [ + 500, + "http://images.neopets.com/items/mall_staff_jewelled.gif", + 2020-01-01T00:00:00.000Z, + "0000000000000000000000000000000000000000000000", + "43397", + ], + ], + Array [ + "UPDATE item_translations SET description = ?, name = ?, updated_at = ? WHERE item_id = ? AND locale = \\"en\\" LIMIT 1;", + Array [ + "This jewelled staff shines with a magical light.", + "Jewelled Staff", + 2020-01-01T00:00:00.000Z, + "43397", + ], + ], + Array [ + "INSERT INTO parents_swf_assets (parent_type, parent_id, swf_asset_id) + VALUES (?, ?, (SELECT id FROM swf_assets WHERE type = \\"object\\" AND remote_id = ?)), (?, ?, (SELECT id FROM swf_assets WHERE type = \\"object\\" AND remote_id = ?)), (?, ?, (SELECT id FROM swf_assets WHERE type = \\"object\\" AND remote_id = ?)), (?, ?, (SELECT id FROM swf_assets WHERE type = \\"object\\" AND remote_id = ?)), (?, ?, (SELECT id FROM swf_assets WHERE type = \\"object\\" AND remote_id = ?)), (?, ?, (SELECT id FROM swf_assets WHERE type = \\"object\\" AND remote_id = ?)), (?, ?, (SELECT id FROM swf_assets WHERE type = \\"object\\" AND remote_id = ?)), (?, ?, (SELECT id FROM swf_assets WHERE type = \\"object\\" AND remote_id = ?))", + Array [ + "Item", + "37375", + "6829", + "Item", + "38913", + "14855", + "Item", + "38912", + "14856", + "Item", + "38911", + "14857", + "Item", + "43014", + "36414", + "Item", + "43397", + "39646", + "Item", + "37229", + "51959", + "Item", + "48313", + "56478", + ], + ], + Array [ + "SELECT * FROM pet_states WHERE (pet_type_id = ? AND swf_asset_ids = ?)", + Array [ + "1", + "7941,7942,7946,21057,21060,24008", + ], + ], + Array [ + "INSERT INTO pet_states (female, labeled, mood_id, pet_type_id, swf_asset_ids, unconverted) VALUES (?, ?, ?, ?, ?, ?);", + Array [ + 0, + 1, + "2", + "1", + "7941,7942,7946,21057,21060,24008", + 0, + ], + ], + Array [ + "SELECT * FROM pet_states WHERE (pet_type_id = ? AND swf_asset_ids = ?)", + Array [ + "1", + "7941,7942,7946,21057,21060,24008", + ], + ], + Array [ + "SELECT * FROM swf_assets WHERE (type = ? AND remote_id = ?) OR (type = ? AND remote_id = ?) OR (type = ? AND remote_id = ?) OR (type = ? AND remote_id = ?) OR (type = ? AND remote_id = ?) OR (type = ? AND remote_id = ?)", + Array [ + "biology", + "7942", + "biology", + "7941", + "biology", + "24008", + "biology", + "21060", + "biology", + "21057", + "biology", + "7946", + ], + ], + Array [ + "INSERT INTO parents_swf_assets (parent_type, parent_id, swf_asset_id) + VALUES (?, ?, ?), (?, ?, ?), (?, ?, ?), (?, ?, ?), (?, ?, ?), (?, ?, ?);", + Array [ + "PetState", + "1", + "9", + "PetState", + "1", + "10", + "PetState", + "1", + "11", + "PetState", + "1", + "12", + "PetState", + "1", + "13", + "PetState", + "1", + "14", + ], + ], +] +`; + +exports[`Pet models updated item data 2`] = ` +Array [ + Array [ + "SELECT * FROM item_translations WHERE item_id IN (?) AND locale = \\"en\\"", + Array [ + "43397", + ], + ], + Array [ + "SELECT * FROM items WHERE id IN (?)", + Array [ + "43397", + ], + ], +] +`; + +exports[`Pet sets bodyId=0 after seeing it on two body types: db 1`] = ` +Array [ + Array [ + "SELECT * FROM pet_types WHERE (species_id = ? AND color_id = ?)", + Array [ + "54", + "75", + ], + ], + Array [ + "SELECT * FROM items WHERE id IN (?,?,?,?,?,?,?,?)", + Array [ + "37229", + "37375", + "38911", + "38912", + "38913", + "43014", + "43397", + "48313", + ], + ], + Array [ + "SELECT * FROM item_translations WHERE item_id IN (?,?,?,?,?,?,?,?) AND locale = \\"en\\"", + Array [ + "37229", + "37375", + "38911", + "38912", + "38913", + "43014", + "43397", + "48313", + ], + ], + Array [ + "SELECT * FROM swf_assets WHERE (type = ? AND remote_id = ?) OR (type = ? AND remote_id = ?) OR (type = ? AND remote_id = ?) OR (type = ? AND remote_id = ?) OR (type = ? AND remote_id = ?) OR (type = ? AND remote_id = ?) OR (type = ? AND remote_id = ?) OR (type = ? AND remote_id = ?) OR (type = ? AND remote_id = ?) OR (type = ? AND remote_id = ?) OR (type = ? AND remote_id = ?) OR (type = ? AND remote_id = ?) OR (type = ? AND remote_id = ?) OR (type = ? AND remote_id = ?)", + Array [ + "object", + "6829", + "object", + "14855", + "object", + "14856", + "object", + "14857", + "object", + "36414", + "object", + "39646", + "object", + "51959", + "object", + "56478", + "biology", + "7942", + "biology", + "7941", + "biology", + "24008", + "biology", + "21060", + "biology", + "21057", + "biology", + "7946", + ], + ], + Array [ + "INSERT INTO pet_types (body_id, color_id, created_at, species_id) VALUES (?, ?, ?, ?);", + Array [ + "180", + "75", + 2020-01-01T00:00:00.000Z, + "54", + ], + ], + Array [ + "INSERT INTO items (category, created_at, id, price, rarity_index, thumbnail_url, type, updated_at, weight_lbs, zones_restrict) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?, ?, ?, ?);", + Array [ + "Gift", + 2020-01-01T00:00:00.000Z, + "37229", + 0, + 101, + "http://images.neopets.com/items/gif_magicball_table.gif", + "Gift", + 2020-01-01T00:00:00.000Z, + 1, + "0000000000000000000000000000000000000000000000", + "Special", + 2020-01-01T00:00:00.000Z, + "37375", + 209, + 75, + "http://images.neopets.com/items/bg_moonstars.gif", + "Mystical Surroundings", + 2020-01-01T00:00:00.000Z, + 1, + "0000000000000000000000000000000000000000000000000000", + "Clothes", + 2020-01-01T00:00:00.000Z, + "38911", + 980, + 92, + "http://images.neopets.com/items/clo_zafara_agent_hood.gif", + "Clothes", + 2020-01-01T00:00:00.000Z, + 1, + "0000000000000000000000000000000000001100000000000000", + "Clothes", + 2020-01-01T00:00:00.000Z, + "38912", + 1476, + 90, + "http://images.neopets.com/items/clo_zafara_agent_robe.gif", + "Clothes", + 2020-01-01T00:00:00.000Z, + 1, + "0000000000000000000101000000000000000000000000000000", + "Clothes", + 2020-01-01T00:00:00.000Z, + "38913", + 1177, + 88, + "http://images.neopets.com/items/clo_zafara_agent_gloves.gif", + "Clothes", + 2020-01-01T00:00:00.000Z, + 1, + "0000000000000000000000000000000000000000000000000000", + "Toys", + 2020-01-01T00:00:00.000Z, + "43014", + 1033, + 80, + "http://images.neopets.com/items/toy_stringlight_illleaf.gif", + "Toy", + 2020-01-01T00:00:00.000Z, + 1, + "0000000000000000000000000000000000000000000000", + "Clothes", + 2020-01-01T00:00:00.000Z, + "43397", + 0, + 500, + "http://images.neopets.com/items/mall_staff_jewelled.gif", + "Clothes", + 2020-01-01T00:00:00.000Z, + 1, + "0000000000000000000000000000000000000000000000", + "Clothes", + 2020-01-01T00:00:00.000Z, + "48313", + 0, + 101, + "http://images.neopets.com/items/clo_altcuplogo_brooch.gif", + "Clothes", + 2020-01-01T00:00:00.000Z, + 1, + "0000000000000000000000000000000000000000000000000000", + ], + ], + Array [ + "INSERT INTO item_translations (created_at, description, item_id, locale, name, rarity, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?);", + Array [ + 2020-01-01T00:00:00.000Z, + "What does this ball actually do?", + "37229", + "en", + "Magic Ball Table", + "Special", + 2020-01-01T00:00:00.000Z, + 2020-01-01T00:00:00.000Z, + "Dont forget to wish upon a star.", + "37375", + "en", + "Moon and Stars Background", + "Uncommon", + 2020-01-01T00:00:00.000Z, + 2020-01-01T00:00:00.000Z, + "Hide your face and hair so no one can recognise you.", + "38911", + "en", + "Zafara Agent Hood", + "Very Rare", + 2020-01-01T00:00:00.000Z, + 2020-01-01T00:00:00.000Z, + "This robe is great for being stealthy.", + "38912", + "en", + "Zafara Agent Robe", + "Very Rare", + 2020-01-01T00:00:00.000Z, + 2020-01-01T00:00:00.000Z, + "Dont leave any trace that you were there with these gloves.", + "38913", + "en", + "Zafara Agent Gloves", + "Rare", + 2020-01-01T00:00:00.000Z, + 2020-01-01T00:00:00.000Z, + "These leaves almost look magical with their gentle glow.", + "43014", + "en", + "Green Leaf String Lights", + "Uncommon", + 2020-01-01T00:00:00.000Z, + 2020-01-01T00:00:00.000Z, + "This jewelled staff shines with a magical light.", + "43397", + "en", + "Jewelled Staff", + "Artifact", + 2020-01-01T00:00:00.000Z, + 2020-01-01T00:00:00.000Z, + "Even the announcers of the Altador Cup celebrate. This was given out by the Advent Calendar in Y11.", + "48313", + "en", + "Altador Cup Brooch", + "Special", + 2020-01-01T00:00:00.000Z, + ], + ], + Array [ + "INSERT INTO swf_assets (body_id, created_at, remote_id, type, url, zone_id, zones_restrict) VALUES (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?);", + Array [ + "180", + 2020-01-01T00:00:00.000Z, + "14855", + "object", + "http://images.neopets.com/cp/items/swf/000/000/014/14855_215f367070.swf", + "25", + "", + "180", + 2020-01-01T00:00:00.000Z, + "14856", + "object", + "http://images.neopets.com/cp/items/swf/000/000/014/14856_46c1b32797.swf", + "26", + "", + "180", + 2020-01-01T00:00:00.000Z, + "14857", + "object", + "http://images.neopets.com/cp/items/swf/000/000/014/14857_d43380ef66.swf", + "40", + "", + "180", + 2020-01-01T00:00:00.000Z, + "36414", + "object", + "http://images.neopets.com/cp/items/swf/000/000/036/36414_1e2aaab4ad.swf", + "48", + "", + "180", + 2020-01-01T00:00:00.000Z, + "39646", + "object", + "http://images.neopets.com/cp/items/swf/000/000/039/39646_e129e22ada.swf", + "42", + "", + "180", + 2020-01-01T00:00:00.000Z, + "51959", + "object", + "http://images.neopets.com/cp/items/swf/000/000/051/51959_4439727c48.swf", + "45", + "", + "180", + 2020-01-01T00:00:00.000Z, + "56478", + "object", + "http://images.neopets.com/cp/items/swf/000/000/056/56478_eabc28e7c7.swf", + "27", + "", + "0", + 2020-01-01T00:00:00.000Z, + "7942", + "biology", + "http://images.neopets.com/cp/bio/swf/000/000/007/7942_2eab06fd7b.swf", + "5", + "0000000000000000000000000000000000000000000000000000", + "0", + 2020-01-01T00:00:00.000Z, + "7941", + "biology", + "http://images.neopets.com/cp/bio/swf/000/000/007/7941_2c4cc4b846.swf", + "15", + "0000000000000000000000000000000000000000000000000000", + "0", + 2020-01-01T00:00:00.000Z, + "24008", + "biology", + "http://images.neopets.com/cp/bio/swf/000/000/024/24008_a05fe9876a.swf", + "30", + "0000000000000000000000000000000000000000000000000000", + "0", + 2020-01-01T00:00:00.000Z, + "21060", + "biology", + "http://images.neopets.com/cp/bio/swf/000/000/021/21060_d77ba93b7b.swf", + "33", + "0000000000000000000000000000000000000000000000000000", + "0", + 2020-01-01T00:00:00.000Z, + "21057", + "biology", + "http://images.neopets.com/cp/bio/swf/000/000/021/21057_4550efbb2f.swf", + "34", + "0000000000000000000000000000000000000000000000000000", + "0", + 2020-01-01T00:00:00.000Z, + "7946", + "biology", + "http://images.neopets.com/cp/bio/swf/000/000/007/7946_0348dad587.swf", + "37", + "0000000000000000000000000000000000000000000000000000", + ], + ], + Array [ + "SELECT * FROM pet_types WHERE (species_id = ? AND color_id = ?)", + Array [ + "54", + "75", + ], + ], + Array [ + "INSERT INTO parents_swf_assets (parent_type, parent_id, swf_asset_id) + VALUES (?, ?, (SELECT id FROM swf_assets WHERE type = \\"object\\" AND remote_id = ?)), (?, ?, (SELECT id FROM swf_assets WHERE type = \\"object\\" AND remote_id = ?)), (?, ?, (SELECT id FROM swf_assets WHERE type = \\"object\\" AND remote_id = ?)), (?, ?, (SELECT id FROM swf_assets WHERE type = \\"object\\" AND remote_id = ?)), (?, ?, (SELECT id FROM swf_assets WHERE type = \\"object\\" AND remote_id = ?)), (?, ?, (SELECT id FROM swf_assets WHERE type = \\"object\\" AND remote_id = ?)), (?, ?, (SELECT id FROM swf_assets WHERE type = \\"object\\" AND remote_id = ?))", + Array [ + "Item", + "38913", + "14855", + "Item", + "38912", + "14856", + "Item", + "38911", + "14857", + "Item", + "43014", + "36414", + "Item", + "43397", + "39646", + "Item", + "37229", + "51959", + "Item", + "48313", + "56478", + ], + ], + Array [ + "SELECT * FROM pet_states WHERE (pet_type_id = ? AND swf_asset_ids = ?)", + Array [ + "1", + "7941,7942,7946,21057,21060,24008", + ], + ], + Array [ + "UPDATE swf_assets SET body_id = ? WHERE type = ? AND remote_id = ? LIMIT 1;", + Array [ + "0", + "object", + "6829", + ], + ], + Array [ + "INSERT INTO pet_states (female, labeled, mood_id, pet_type_id, swf_asset_ids, unconverted) VALUES (?, ?, ?, ?, ?, ?);", + Array [ + 0, + 1, + "2", + "1", + "7941,7942,7946,21057,21060,24008", + 0, + ], + ], + Array [ + "SELECT * FROM pet_states WHERE (pet_type_id = ? AND swf_asset_ids = ?)", + Array [ + "1", + "7941,7942,7946,21057,21060,24008", + ], + ], + Array [ + "SELECT * FROM swf_assets WHERE (type = ? AND remote_id = ?) OR (type = ? AND remote_id = ?) OR (type = ? AND remote_id = ?) OR (type = ? AND remote_id = ?) OR (type = ? AND remote_id = ?) OR (type = ? AND remote_id = ?)", + Array [ + "biology", + "7942", + "biology", + "7941", + "biology", + "24008", + "biology", + "21060", + "biology", + "21057", + "biology", + "7946", + ], + ], + Array [ + "INSERT INTO parents_swf_assets (parent_type, parent_id, swf_asset_id) + VALUES (?, ?, ?), (?, ?, ?), (?, ?, ?), (?, ?, ?), (?, ?, ?), (?, ?, ?);", + Array [ + "PetState", + "1", + "9", + "PetState", + "1", + "10", + "PetState", + "1", + "11", + "PetState", + "1", + "12", + "PetState", + "1", + "13", + "PetState", + "1", + "14", + ], + ], +] +`; diff --git a/src/server/query-tests/setup.js b/src/server/query-tests/setup.js index 82548e3..608cb2d 100644 --- a/src/server/query-tests/setup.js +++ b/src/server/query-tests/setup.js @@ -83,6 +83,7 @@ beforeEach(() => { } dbEnvironment = "production"; dbSetupDone = false; + db = null; }); afterAll(() => { if (db) { @@ -138,7 +139,7 @@ module.exports = { query, getDbCalls, clearDbCalls, - getDb: () => db, + connectToDb, useTestDb, logInAsTestUser, }; diff --git a/src/server/types/AppearanceLayer.js b/src/server/types/AppearanceLayer.js index 59b40ac..afa6780 100644 --- a/src/server/types/AppearanceLayer.js +++ b/src/server/types/AppearanceLayer.js @@ -71,7 +71,7 @@ const typeDefs = gql` const resolvers = { AppearanceLayer: { - bodyId: async ({ id }, _, { swfAssetLoader }) => { + remoteId: async ({ id }, _, { swfAssetLoader }) => { const layer = await swfAssetLoader.load(id); return layer.remoteId; }, @@ -79,7 +79,7 @@ const resolvers = { const layer = await swfAssetLoader.load(id); return layer.bodyId; }, - zone: async ({ id }, _, { swfAssetLoader, zoneLoader }) => { + zone: async ({ id }, _, { swfAssetLoader }) => { const layer = await swfAssetLoader.load(id); return { id: layer.zoneId }; }, @@ -94,7 +94,12 @@ const resolvers = { imageUrl: async ({ id }, { size }, { swfAssetLoader }) => { const layer = await swfAssetLoader.load(id); - if (!layer.hasImage) { + // If there's no image, return null. (In the development db, which isn't + // aware which assets we have images for on the DTI CDN, assume we _do_ + // have the image - it's usually true, and better for testing.) + const hasImage = + layer.hasImage || process.env["DB_ENV"] === "development"; + if (!hasImage) { return null; } @@ -234,12 +239,13 @@ async function loadAndCacheAssetManifest(db, layer) { // // TODO: Someday the manifests will all exist, right? So we'll want to // reload all the missing ones at that time. - manifest = manifest || ""; + const manifestJson = manifest ? JSON.stringify(manifest) : ""; + const [ result, ] = await db.execute( `UPDATE swf_assets SET manifest = ? WHERE id = ? LIMIT 1;`, - [manifest, layer.id] + [manifestJson, layer.id] ); if (result.affectedRows !== 1) { throw new Error( diff --git a/src/server/types/Outfit.js b/src/server/types/Outfit.js index 2422f6f..19c61c4 100644 --- a/src/server/types/Outfit.js +++ b/src/server/types/Outfit.js @@ -1,4 +1,3 @@ -const fetch = require("node-fetch"); const { gql } = require("apollo-server"); const typeDefs = gql` @@ -8,16 +7,10 @@ const typeDefs = gql` petAppearance: PetAppearance! wornItems: [Item!]! closetedItems: [Item!]! - - species: Species! # to be deprecated? can use petAppearance? 🤔 - color: Color! # to be deprecated? can use petAppearance? 🤔 - pose: Pose! # to be deprecated? can use petAppearance? 🤔 - items: [Item!]! # deprecated alias for wornItems } extend type Query { outfit(id: ID!): Outfit - petOnNeopetsDotCom(petName: String!): Outfit } `; @@ -46,242 +39,7 @@ const resolvers = { }, Query: { outfit: (_, { id }) => ({ id }), - petOnNeopetsDotCom: async ( - _, - { petName }, - { db, itemLoader, itemTranslationLoader } - ) => { - // Start all these requests as soon as possible... - const petMetaDataPromise = loadPetMetaData(petName); - const customPetDataPromise = loadCustomPetData(petName); - const modelingPromise = customPetDataPromise.then((customPetData) => - saveModelingData(customPetData, { - db, - itemLoader, - itemTranslationLoader, - }) - ); - - // ...then wait on all of them before finishing. It's important to wait - // on modeling, so that it doesn't get cut off when the request ends! - const [petMetaData, customPetData, __] = await Promise.all([ - petMetaDataPromise, - customPetDataPromise, - modelingPromise, - ]); - - const outfit = { - // TODO: This isn't a fully-working Outfit object. It works for the - // client as currently implemented, but we'll probably want to - // move the client and this onto our more generic fields! - species: { id: customPetData.custom_pet.species_id }, - color: { id: customPetData.custom_pet.color_id }, - pose: getPoseFromPetData(petMetaData, customPetData), - items: Object.values(customPetData.object_info_registry).map((o) => ({ - id: o.obj_info_id, - name: o.name, - description: o.description, - thumbnailUrl: o.thumbnail_url, - rarityIndex: o.rarity_index, - })), - }; - - return outfit; - }, }, }; -async function loadPetMetaData(petName) { - const url = - `http://www.neopets.com/amfphp/json.php/PetService.getPet` + `/${petName}`; - const res = await fetch(url); - if (!res.ok) { - throw new Error( - `for pet meta data, neopets.com returned: ` + - `${res.status} ${res.statusText}. (${url})` - ); - } - - const json = await res.json(); - return json; -} - -async function loadCustomPetData(petName) { - const url = - `http://www.neopets.com/amfphp/json.php/CustomPetService.getViewerData` + - `/${petName}`; - const res = await fetch(url); - if (!res.ok) { - throw new Error( - `for custom pet data, neopets.com returned: ` + - `${res.status} ${res.statusText}. (${url})` - ); - } - - const json = await res.json(); - if (!json.custom_pet) { - throw new Error(`missing custom_pet data`); - } - - return json; -} - -function getPoseFromPetData(petMetaData, petCustomData) { - // TODO: Use custom data to decide if Unconverted. - const moodId = petMetaData.mood; - const genderId = petMetaData.gender; - if (String(moodId) === "1" && String(genderId) === "1") { - return "HAPPY_MASC"; - } else if (String(moodId) === "1" && String(genderId) === "2") { - return "HAPPY_FEM"; - } else if (String(moodId) === "2" && String(genderId) === "1") { - return "SAD_MASC"; - } else if (String(moodId) === "2" && String(genderId) === "2") { - return "SAD_FEM"; - } else if (String(moodId) === "4" && String(genderId) === "1") { - return "SICK_MASC"; - } else if (String(moodId) === "4" && String(genderId) === "2") { - return "SICK_FEM"; - } else { - throw new Error( - `could not identify pose: ` + - `moodId=${moodId}, ` + - `genderId=${genderId}` - ); - } -} - -async function saveModelingData( - customPetData, - { db, itemLoader, itemTranslationLoader } -) { - const itemIds = Object.keys(customPetData.object_info_registry); - const [items, itemTranslations] = await Promise.all([ - itemLoader.loadMany(itemIds), - itemTranslationLoader.loadMany(itemIds), - ]); - - const rowsToInsert = []; - const rowsToUpdate = []; - for (const index in itemIds) { - const itemId = itemIds[index]; - const item = items[index]; - const itemTranslation = itemTranslations[index]; - - const objectInfo = customPetData.object_info_registry[itemId]; - const objectInfoFields = { - id: itemId, - zonesRestrict: objectInfo.zones_restrict, - thumbnailUrl: objectInfo.thumbnail_url, - category: objectInfo.category, - type: objectInfo.type, - rarityIndex: objectInfo.rarity_index, - price: objectInfo.price, - weightLbs: objectInfo.weight_lbs, - name: objectInfo.name, - description: objectInfo.description, - rarity: objectInfo.rarity, - }; - - if (item instanceof Error) { - // New item, we'll just insert it! - rowsToInsert.push({ - ...objectInfoFields, - createdAt: new Date(), - updatedAt: new Date(), - }); - continue; - } - - const itemFields = { - id: item.id, - zonesRestrict: item.zonesRestrict, - thumbnailUrl: item.thumbnailUrl, - category: item.category, - type: item.type, - rarityIndex: item.rarityIndex, - price: item.price, - weightLbs: item.weightLbs, - name: itemTranslation.name, - description: itemTranslation.description, - rarity: itemTranslation.rarity, - }; - - if (objectsShallowEqual(objectInfoFields, itemFields)) { - // Existing item, no change! - continue; - } - - // Updated item, so we'll update it! - rowsToUpdate.push({ - ...objectInfoFields, - updatedAt: new Date(), - }); - } - - if (rowsToInsert.length > 0) { - const itemQs = rowsToInsert - .map((_) => "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)") - .join(", "); - const itemTranslationQs = rowsToInsert - .map((_) => "(?, ?, ?, ?, ?, ?, ?)") - .join(", "); - const itemValues = rowsToInsert.map((row) => [ - row.id, - row.zonesRestrict, - row.thumbnailUrl, - row.category, - row.type, - row.rarityIndex, - row.price, - row.weightLbs, - row.createdAt, - row.updatedAt, - ]); - const itemTranslationValues = rowsToInsert.map((row) => [ - row.id, - "en", - row.name, - row.description, - row.rarity, - row.createdAt, - row.updatedAt, - ]); - - // NOTE: Hmm, I tried to use multiple statements to combine these, but I - // guess it doesn't work for prepared statements? - await Promise.all([ - db.execute( - `INSERT INTO items - ( - id, zones_restrict, thumbnail_url, category, type, rarity_index, - price, weight_lbs, created_at, updated_at - ) - VALUES ${itemQs}; - `, - itemValues.flat() - ), - db.execute( - `INSERT INTO item_translations - (item_id, locale, name, description, rarity, created_at, updated_at) - VALUES ${itemTranslationQs};`, - itemTranslationValues.flat() - ), - ]); - } - - // TODO: Update the items that need updating! -} - -/** Given two objects with the same keys, return whether their values match. */ -function objectsShallowEqual(a, b) { - for (const key in a) { - if (a[key] !== b[key]) { - return false; - } - } - - return true; -} - module.exports = { typeDefs, resolvers }; diff --git a/src/server/types/Pet.js b/src/server/types/Pet.js new file mode 100644 index 0000000..31f6acf --- /dev/null +++ b/src/server/types/Pet.js @@ -0,0 +1,150 @@ +const fetch = require("node-fetch"); +const { gql } = require("apollo-server"); + +const { getPoseFromPetState } = require("../util"); +const { saveModelingData } = require("../modeling"); + +const typeDefs = gql` + type Pet { + id: ID! + name: String! + petAppearance: PetAppearance! + wornItems: [Item!]! + + species: Species! # to be deprecated? can use petAppearance? 🤔 + color: Color! # to be deprecated? can use petAppearance? 🤔 + pose: Pose! # to be deprecated? can use petAppearance? 🤔 + items: [Item!]! # deprecated alias for wornItems + } + + extend type Query { + petOnNeopetsDotCom(petName: String!): Pet + } +`; + +const resolvers = { + Pet: { + species: ({ customPetData }) => ({ + id: customPetData.custom_pet.species_id, + }), + color: ({ customPetData }) => ({ id: customPetData.custom_pet.color_id }), + pose: ({ customPetData, petMetaData }) => + getPoseFromPetData(petMetaData, customPetData), + petAppearance: async ( + { customPetData, petMetaData }, + _, + { petTypeBySpeciesAndColorLoader, petStatesForPetTypeLoader } + ) => { + const petType = await petTypeBySpeciesAndColorLoader.load({ + speciesId: customPetData.custom_pet.species_id, + colorId: customPetData.custom_pet.color_id, + }); + const petStates = await petStatesForPetTypeLoader.load(petType.id); + const pose = getPoseFromPetData(petMetaData, customPetData); + const petState = petStates.find((ps) => getPoseFromPetState(ps) === pose); + return { id: petState.id }; + }, + wornItems: ({ customPetData }) => + Object.values(customPetData.object_info_registry).map((o) => ({ + id: o.obj_info_id, + name: o.name, + description: o.description, + thumbnailUrl: o.thumbnail_url, + rarityIndex: o.rarity_index, + })), + items: (...args) => resolvers.Pet.wornItems(...args), + }, + Query: { + outfit: (_, { id }) => ({ id }), + petOnNeopetsDotCom: async ( + _, + { petName }, + { + db, + petTypeBySpeciesAndColorLoader, + petStateByPetTypeAndAssetsLoader, + itemLoader, + itemTranslationLoader, + swfAssetByRemoteIdLoader, + } + ) => { + const [customPetData, petMetaData, __] = await Promise.all([ + loadCustomPetData(petName), + loadPetMetaData(petName), + ]); + + await saveModelingData(customPetData, petMetaData, { + db, + petTypeBySpeciesAndColorLoader, + petStateByPetTypeAndAssetsLoader, + itemLoader, + itemTranslationLoader, + swfAssetByRemoteIdLoader, + }); + + return { name: petName, customPetData, petMetaData }; + }, + }, +}; + +async function loadPetMetaData(petName) { + const url = `http://www.neopets.com/amfphp/json.php/PetService.getPet/${petName}`; + const res = await fetch(url); + if (!res.ok) { + throw new Error( + `for pet meta data, neopets.com returned: ` + + `${res.status} ${res.statusText}. (${url})` + ); + } + + const json = await res.json(); + return json; +} + +async function loadCustomPetData(petName) { + const url = + `http://www.neopets.com/amfphp/json.php/CustomPetService.getViewerData` + + `/${petName}`; + const res = await fetch(url); + if (!res.ok) { + throw new Error( + `for custom pet data, neopets.com returned: ` + + `${res.status} ${res.statusText}. (${url})` + ); + } + + const json = await res.json(); + if (!json.custom_pet) { + throw new Error(`missing custom_pet data`); + } + + return json; +} + +function getPoseFromPetData(petMetaData, petCustomData) { + const moodId = petMetaData.mood; + const genderId = petMetaData.gender; + if (Object.keys(petCustomData.custom_pet.biology_by_zone).length === 1) { + return "UNCONVERTED"; + } else if (String(moodId) === "1" && String(genderId) === "1") { + return "HAPPY_MASC"; + } else if (String(moodId) === "1" && String(genderId) === "2") { + return "HAPPY_FEM"; + } else if (String(moodId) === "2" && String(genderId) === "1") { + return "SAD_MASC"; + } else if (String(moodId) === "2" && String(genderId) === "2") { + return "SAD_FEM"; + } else if (String(moodId) === "4" && String(genderId) === "1") { + return "SICK_MASC"; + } else if (String(moodId) === "4" && String(genderId) === "2") { + return "SICK_FEM"; + } else { + throw new Error( + `could not identify pose: ` + + `moodId=${moodId}, ` + + `genderId=${genderId}` + ); + } +} + +module.exports = { typeDefs, resolvers }; diff --git a/src/server/types/PetAppearance.js b/src/server/types/PetAppearance.js index 7bc9233..9f2fa9f 100644 --- a/src/server/types/PetAppearance.js +++ b/src/server/types/PetAppearance.js @@ -106,6 +106,17 @@ const resolvers = { speciesId: id, colorId: "8", // Blue }); + + // In production, this should ~never happen, because all species have a + // Blue version, or at least if a new one is added it will be modeled + // quickly! But in development, before modeling happens, it's possible + // for this to be empty, so we return a fake body ID. (This seems better + // than making it nullable, which adds downstream complexity for a + // particularly edge-y case that generally isn't worth considering.) + if (!petType) { + return ``; + } + return petType.bodyId; }, }, @@ -208,6 +219,9 @@ const resolvers = { speciesId, colorId, }); + if (!petType) { + return null; + } // TODO: We could query for this more directly, instead of loading all // appearances 🤔