Initial release
authorJoann Mõndresku <joann@cernodile.com>
Sat, 11 May 2024 09:01:27 +0000 (12:01 +0300)
committerJoann Mõndresku <joann@cernodile.com>
Sat, 11 May 2024 09:01:27 +0000 (12:01 +0300)
LICENSE.txt [new file with mode: 0644]
README.md [new file with mode: 0644]
app.py [new file with mode: 0644]
contracts.py [new file with mode: 0644]
data/README.txt [new file with mode: 0644]
data/contract-creator.py [new file with mode: 0755]
data/contracts.json [new file with mode: 0644]
ei_pb2.py [new file with mode: 0644]
events.py [new file with mode: 0644]
requirements.txt [new file with mode: 0644]

diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644 (file)
index 0000000..061d802
--- /dev/null
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2024 cernodile.com
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644 (file)
index 0000000..659f981
--- /dev/null
+++ b/README.md
@@ -0,0 +1,37 @@
+# reEgg
+A server emulator for Auxbrain's Egg, Inc. mobile game, written in Python, for version 1.12.13.
+This project was created for [Reverse Engineering a Mobile App Protobuf API](https://based.quest/reverse-engineering-a-mobile-app-protobuf-api/) blog posts on based.quest.
+
+## DISCLAIMER
+The version of game chosen is deliberate to not affect the current live service. The game developer has had a history of having to
+deal with cheaters and I do not wish to furthen the problem by attempting to reverse engineer their efforts against cheaters.
+The project's scope is to educate people on how to reverse engineer APIs in order for digital preservation and archival of media.
+As API servers shut down, many apps are immediately locked out from being useable or get heavily hindered in capabilities to do anything productive.
+
+## Setup
+Get any webserver that supports reverse proxying. Point it to proxy http://127.0.0.1:5000/. Create self-signed CA and certificate for www.auxbrain.com and configure your chosen webserver to use it.
+Once that is set up, install "protobuf" and "flask" with pip and run `flask run` on the command line.
+
+You will need a VPN app on your phone that can overwrite DNS records (such as AdAway) and root to perform SSL unpinning (Xposed Framework has modules for that).
+You can also perform this without root by repacking the app with a modified manifest.
+
+Once you've setup redirection, install the self-signed CA to Android's CA store.
+
+## Configuration
+You might want to check out `data/` directory and check for any epoch values. I am using 05/05/2024 as the default epoch for scheduling contracts for example.
+
+## Roadmap
+- [ ] First contact
+  - [ ] Offer a valid backup
+  - [x] Respond with valid payload
+  - [x] Unlock Pro Permit
+- [ ] Gift Calendar
+- [ ] Periodicals
+  - [ ] Contracts
+    - [x] Your First Contract
+    - [x] Contract Scheduler
+    - [ ] Co-op with computer simulations
+  - [x] Events
+    - [x] Proof of Concept
+    - [x] Event Scheduler
+
diff --git a/app.py b/app.py
new file mode 100644 (file)
index 0000000..157df32
--- /dev/null
+++ b/app.py
@@ -0,0 +1,67 @@
+############
+# Egg Inc v1.12.13 (Android Build 111121) server emulator
+# Read the blog post at https://based.quest/reverse-engineering-a-mobile-app-protobuf-api/
+############
+import base64
+import time
+
+import ei_pb2 as EIProto
+
+from flask import Flask
+from flask import request
+
+import contracts
+import events
+
+upgrade_cache = {}
+
+app = Flask(__name__)
+contracts.load_contracts()
+
+@app.route('/ei/<path:subpath>', methods=['POST'])
+def ei_routes(subpath):
+       print("REQ: /ei/" + subpath)
+       if subpath == "daily_gift_info":
+               print(request.form)
+       data = base64.b64decode(request.form["data"].replace(" ", "+"))
+       if subpath == "first_contact":
+               ContactReq = EIProto.EggIncFirstContactRequest()
+               ContactReq.ParseFromString(data)
+               ContactResp = EIProto.EggIncFirstContactResponse()
+               if ContactReq.user_id in upgrade_cache:
+                       print("Found an unupgraded save - lets upgrade the permit level to Pro")
+                       ContactResp.backup.CopyFrom(cache[ContactReq.user_id])
+                       del upgrade_cache[ContactReq.user_id]
+               return base64.b64encode(ContactResp.SerializeToString())
+       elif subpath == "save_backup":
+               SaveBackup = EIProto.Backup()
+               SaveBackup.ParseFromString(bytes(data))
+               if SaveBackup.game.permit_level == 0:
+                       SaveBackup.game.permit_level = 1
+                       SaveBackup.force_backup = True
+                       SaveBackup.force_offer_backup = True
+                       upgrade_cache[SaveBackup.user_id] = SaveBackup
+       elif subpath == "get_periodicals":
+               PeriodicalResp = EIProto.PeriodicalsResponse()
+               for evt in events.get_active_events():
+                       e = PeriodicalResp.events.events.add()
+                       e.CopyFrom(evt)
+               for contract in contracts.get_active_contracts():
+                       c = PeriodicalResp.contracts.contracts.add()
+                       c.CopyFrom(contract)
+               return base64.b64encode(PeriodicalResp.SerializeToString())
+       else:
+               print("DATA", base64.b64encode(data))
+       return ""
+
+@app.route('/ei_data/<path:subpath>', methods=['POST'])
+def ei_data_rotues(subpath):
+       print("REQ /ei_data/" + subpath)
+       if subpath == "log_action":
+               data = base64.b64decode(request.form["data"])
+               GenericAction = EIProto.GenericAction()
+               GenericAction.ParseFromString(data)
+               print(GenericAction)
+       else:
+               print(request.form)
+       return ""
diff --git a/contracts.py b/contracts.py
new file mode 100644 (file)
index 0000000..17d2bbf
--- /dev/null
@@ -0,0 +1,95 @@
+# contracts.py - Contract Definitions parser and provider
+import ei_pb2 as EIProto
+import json
+import time
+
+# Keep a cache of all contracts
+contract_epoch = int(time.time())
+global_contract_db = {"legacy": [], "normal": [], "permanent": []}
+
+def get_active_contracts():
+       # TODO: Shift the epoch when a full run of leggacys is done.
+       # TODO: Don't iterate all the contracts, calculate where appropriate on starting index
+       # Return an array of Contract objects
+       list = [global_contract_db["permanent"][0]]
+       # Iterate leggacy
+       time_since_epoch = time.time() - contract_epoch
+       i = 0
+       for contract in global_contract_db["legacy"]:
+               # Leggacy ones are active for a week, one per week
+               i += 1
+               expiry_time = (604800 * i) - time_since_epoch
+               # It's expired, get the next one
+               if expiry_time < 0:
+                       continue
+               # It's already next week's contract, don't process more
+               if expiry_time > 604800:
+                       break
+               contract.expiration_time = expiry_time
+               list.append(contract)
+       # Add regular contracts here when implemented
+       # DESIGN QUESTION: Do we even *want* regular contracts? Could just run two "leggacy" branches in parallel.
+       return list
+
+def __convert_contract_to_proto(obj):
+       # Map values from JSON object to Protobuf object.
+       contract = EIProto.Contract()
+       contract.identifier = obj["id"]
+       contract.name = obj["name"]
+       contract.description = obj["description"]
+       contract.egg = EIProto.Egg.Value(obj["egg"])
+       contract.minutes_per_token = obj["token_interval"]
+       contract.length_seconds = obj["duration"]
+       contract.coop_allowed = False
+       for goal_set_src in obj["goalsets"]:
+               goal_set = contract.goal_sets.add()
+               for goal_src in goal_set_src:
+                       goal = goal_set.goals.add()
+                       goal.type = EIProto.GoalType.EGGS_LAID
+                       goal.target_amount = goal_src["deliver"]
+                       goal.reward_type = EIProto.RewardType.Value(goal_src["reward_type"])
+                       if goal.reward_type == EIProto.RewardType.BOOST:
+                               goal.reward_sub_type = goal_src["reward_str"]
+                       goal.reward_amount = goal_src["reward_amt"]
+       return contract
+
+def load_contracts():
+       with open("data/contracts.json", "r") as file:
+               obj = json.loads(file.read())
+               contract_epoch = obj["epoch"]
+               time_since_epoch = time.time() - contract_epoch
+               i = 0
+               for source in obj["legacy"]:
+                       global_contract_db["legacy"].append(__convert_contract_to_proto(source))
+                       i += 1
+                       global_contract_db["legacy"][-1].expiration_time = (604800 * i) - time_since_epoch
+       print(f'Loaded in {len(global_contract_db["legacy"])} "Leggacy" contracts, {len(global_contract_db["normal"])} to-schedule contracts')
+       return
+
+# Add the permanent contract here as well
+def create_perma_contract():
+       obj = {
+               "id": "first-contract",
+               "name": "Your First Contract",
+               "description": "We heard you are open to contract work! Help fill this order from the local pharmacy!",
+               "egg": "MEDICAL",
+               "duration": 14400.0,
+               "token_interval": 5.0,
+               "goalsets": [
+                       [
+                               {"deliver": 100000.0, "reward_type": "GOLD", "reward_amt": 500},
+                               {"deliver": 5000000000.0, "reward_type": "PIGGY_FILL", "reward_amt": 10000}
+                       ],
+                       [
+                               {"deliver": 100000.0, "reward_type": "GOLD", "reward_amt": 192},
+                               {"deliver": 5000000000.0, "reward_type": "PIGGY_FILL", "reward_amt": 10000}
+                       ]
+               ]
+       }
+       contract = __convert_contract_to_proto(obj)
+       # The permanent "first contract" is a special case where it should never expire and it should also not appear after 5000 Soul Eggs
+       contract.expiration_time = 100000000.0
+       contract.max_soul_eggs = 5000.0
+       return contract
+
+global_contract_db["permanent"].append(create_perma_contract())
diff --git a/data/README.txt b/data/README.txt
new file mode 100644 (file)
index 0000000..3d03ec3
--- /dev/null
@@ -0,0 +1,3 @@
+Contracts sourced from https://eicoop-carpet.netlify.app/
+
+For leggacy: Choose "Type: Leggacy" as filter, start going from Laurel vs Yanny as that's the first one with Standard/Elite
diff --git a/data/contract-creator.py b/data/contract-creator.py
new file mode 100755 (executable)
index 0000000..19993b1
--- /dev/null
@@ -0,0 +1,153 @@
+#!/bin/python
+import json
+import sys
+sys.path.append("..")
+import ei_pb2 as EIProto
+
+#                        [
+#                                {"deliver": 250000000000000000, "reward_type": "SOUL_EGGS", "reward_amt": 250000},
+#                                {"deliver": 500000000000000000, "reward_type": "EGGS_OF_PROPHECY", "reward_amt": 1}
+#                        ],
+#                        [
+#                                {"deliver": 50000000000000, "reward_type": "SOUL_EGGS", "reward_amt": 10000},
+#                                {"deliver": 1000000000000000, "reward_type": "EGGS_OF_PROPHECY", "reward_amt": 1}
+#                        ]
+#                ]
+#        }
+boost_defs = {
+"Supreme tachyon prism": "tachyon_prism_orange_big",
+"Legendary tachyon prism (1000x 10m)": "tachyon_prism_orange",
+"Legendary boost beacon (500x 10m)": "boost_beacon_orange",
+"Legendary soul beacon (50x 10m)": "soul_beacon_orange",
+"Epic tachyon prism (100x 2hr)": "tachyon_prism_purple_big",
+"Epic soul beacon (50x 30)": "soul_beacon_purple",
+"Powerful tachyon prism (100x 20m)": "tachyon_prism_purple",
+"Jimbo's best bird feed (50x 2hr)": "jimbos_orange_big",
+"Jimbo's best bird feed (50x 10m)": "jimbos_orange",
+"Large boost beacon (5x 1hr)": "boost_beacon_blue_big"
+}
+
+obj = {"id": "", "name": "", "description": "", "duration": 0.0, "token_interval": 0.0, "egg": "", "goalsets": []}
+
+def ask_for_string(preprint):
+       while True:
+               print(preprint, end="")
+               x = input()
+               if len(x) > 0:
+                       return x
+
+def ask_for_float(preprint):
+       while True:
+               print(preprint, end="")
+               x = input()
+               try:
+                       val = float(x)
+                       return val
+               except:
+                       continue
+
+def ask_for_quantity(preprint):
+       while True:
+               print(preprint, end="")
+               x = input()
+               vals = x.split(" ")
+               if len(vals) == 2:
+                       try:
+                               val = int(vals[0])
+                               # why doesnt python have switch statements?
+                               multip = vals[1]
+                               if len(multip) != 1:
+                                       print("Your quantifier must be 1 letter in length")
+                                       continue
+                               if multip.upper() == "M":
+                                       val *= 1000000
+                               elif multip.upper() == "B":
+                                       val *= 1000000000
+                               elif multip.upper() == "T":
+                                       val *= 1000000000000
+                               elif multip == "q":
+                                       val *= 1000000000000000
+                               elif multip == "Q":
+                                       val *= 1000000000000000000
+                               elif multip == 's':
+                                       val *= 1000000000000000000000
+                               elif multip == 'S':
+                                       val *= 1000000000000000000000000
+                               else:
+                                       print("Couldn't identify your quantifier")
+                                       continue
+                               return val
+                       except:
+                               continue
+               else:
+                       print("Please enter the value in following format: VALUE QUANTIFIER (Example: 10 q)")
+
+def parse_egg_type(preprint):
+       while True:
+               print(preprint, end="")
+               x = input()
+               if x == "?":
+                       print(", ".join(EIProto.Egg.keys()))
+               else:
+                       try:
+                               egg_type = x.upper()
+                               EIProto.Egg.Value(egg_type)
+                               return egg_type
+                       except:
+                               continue
+
+def parse_reward_type(preprint):
+       while True:
+               print(preprint, end="")
+               x = input()
+               if x == "?":
+                       print(", ".join(EIProto.RewardType.keys()))
+               else:
+                       try:
+                               reward_type = x.upper()
+                               EIProto.RewardType.Value(reward_type)
+                               return reward_type
+                       except:
+                               continue
+
+def parse_boost_type(preprint):
+       while True:
+               print(preprint, end="")
+               x = input()
+               if x == "?":
+                       for a, b in list(boost_defs.items()): print(a + ": " + b)
+               else:
+                       try:
+                               boost_name = x.lower()
+                               if boost_name not in boost_defs.values():
+                                       continue
+                               return boost_name
+                       except:
+                               continue
+
+
+def create_goalset(type):
+       print("Creating a " + type + " goalset")
+       goal_amount = int(ask_for_float("How many goals: "))
+       goals = []
+       for i in range(0, goal_amount):
+               print("Goal " + str(i + 1) + "/" + str(goal_amount))
+               goal = {"deliver": 0, "reward_type": "", "reward_str": "", "reward_amt": 0}
+               goal["deliver"] = ask_for_quantity("How many eggs to deliver? ")
+               goal["reward_type"] = parse_reward_type("Enter reward type - type '?' for reference: ")
+               if goal["reward_type"] == "BOOST":
+                       goal["reward_str"] = parse_boost_type("Type in boost type - type '?' for reference: ")
+               goal["reward_amt"] = ask_for_float("How many rewarded? ")
+               goals.append(goal)
+       return goals
+
+obj["id"] = ask_for_string("Enter contract ID: ")
+obj["name"] = ask_for_string("Enter contract name: ")
+obj["description"] = ask_for_string("Enter contract description: ")
+obj["duration"] = ask_for_float("How many minutes does the contract last: ") * 60.0
+obj["token_interval"] = ask_for_float("How many minutes to wait per reward token: ")
+obj["egg"] = parse_egg_type("Enter egg type - type '?' if need reference: ")
+obj["goalsets"].append(create_goalset("Elite"))
+obj["goalsets"].append(create_goalset("Standard"))
+
+print(json.dumps(obj, indent='\t'))
diff --git a/data/contracts.json b/data/contracts.json
new file mode 100644 (file)
index 0000000..53e2930
--- /dev/null
@@ -0,0 +1,116 @@
+{
+  "epoch": 1714867200,
+  "legacy": [
+    {
+      "id": "laurel-v-yanny",
+      "name": "LEGGACY: Laurel vs Yanny",
+      "description": "A Quantum rift is causing people to hear different words in a viral video. Scientists have a fix but they need your help.",
+      "duration": 345600.0,
+      "token_interval": 60.0,
+      "egg": "QUANTUM",
+      "goalsets": [
+        [
+          {
+            "deliver": 250000000000000000,
+            "reward_type": "SOUL_EGGS",
+            "reward_amt": 250000
+          },
+          {
+            "deliver": 500000000000000000,
+            "reward_type": "EGGS_OF_PROPHECY",
+            "reward_amt": 1
+          }
+        ],
+        [
+          {
+            "deliver": 50000000000000,
+            "reward_type": "SOUL_EGGS",
+            "reward_amt": 10000
+          },
+          {
+            "deliver": 1000000000000000,
+            "reward_type": "EGGS_OF_PROPHECY",
+            "reward_amt": 1
+          }
+        ]
+      ]
+    },
+    {
+      "id": "build-a-house",
+      "name": "LEGGACY: Home Renovations",
+      "description": "You've decided to build a new house! Why not make it yourself? You'll need a lot of eggs so get started!",
+      "duration": 864000.0,
+      "token_interval": 60.0,
+      "egg": "SUPER_MATERIAL",
+      "goalsets": [
+        [
+          {
+            "deliver": 5000000000000000,
+            "reward_type": "GOLD",
+            "reward_str": "",
+            "reward_amt": 10000.0
+          },
+          {
+            "deliver": 25000000000000000,
+            "reward_type": "BOOST",
+            "reward_str": "tachyon_prism_orange_big",
+            "reward_amt": 2.0
+          },
+          {
+            "deliver": 100000000000000000,
+            "reward_type": "EGGS_OF_PROPHECY",
+            "reward_str": "",
+            "reward_amt": 1.0
+          }
+        ],
+        [
+          {
+            "deliver": 50000000000000,
+            "reward_type": "GOLD",
+            "reward_str": "",
+            "reward_amt": 2500.0
+          },
+          {
+            "deliver": 250000000000000,
+            "reward_type": "PIGGY_MULTIPLIER",
+            "reward_str": "",
+            "reward_amt": 2.0
+          },
+          {
+            "deliver": 3000000000000000,
+            "reward_type": "EGGS_OF_PROPHECY",
+            "reward_str": "",
+            "reward_amt": 1.0
+          }
+        ]
+      ]
+    },
+    {
+      "id": "all-or-nothing",
+      "name": "LEGGACY: All or Nothing",
+      "description": "A mysterious investor is looking for a lot of edible eggs.\nBut he wants the full amount or nothing.",
+      "duration": 799980.0,
+      "token_interval": 60.0,
+      "egg": "EDIBLE",
+      "goalsets": [
+        [
+          {
+            "deliver": 250000000000000000,
+            "reward_type": "EGGS_OF_PROPHECY",
+            "reward_str": "",
+            "reward_amt": 1.0
+          }
+        ],
+        [
+          {
+            "deliver": 10000000000000000,
+            "reward_type": "EGGS_OF_PROPHECY",
+            "reward_str": "",
+            "reward_amt": 1.0
+          }
+        ]
+      ]
+    }
+  ],
+  "normal": []
+}
diff --git a/ei_pb2.py b/ei_pb2.py
new file mode 100644 (file)
index 0000000..7150e9f
--- /dev/null
+++ b/ei_pb2.py
@@ -0,0 +1,164 @@
+# -*- coding: utf-8 -*-
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: ei.proto
+# Protobuf Python Version: 4.25.3
+"""Generated protocol buffer code."""
+from google.protobuf import descriptor as _descriptor
+from google.protobuf import descriptor_pool as _descriptor_pool
+from google.protobuf import symbol_database as _symbol_database
+from google.protobuf.internal import builder as _builder
+# @@protoc_insertion_point(imports)
+
+_sym_db = _symbol_database.Default()
+
+
+
+
+DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x08\x65i.proto\x12\x02\x65i\"\xaf&\n\x06\x42\x61\x63kup\x12\x0f\n\x07user_id\x18\x01 \x01(\t\x12\x11\n\tuser_name\x18\x02 \x01(\t\x12\x13\n\x0b\x61pprox_time\x18\x03 \x01(\x01\x12\x12\n\x07version\x18\x15 \x01(\r:\x01\x30\x12\x1a\n\x12\x66orce_offer_backup\x18\x14 \x01(\x08\x12\x14\n\x0c\x66orce_backup\x18\x16 \x01(\x08\x12%\n\x08settings\x18\x04 \x01(\x0b\x32\x13.ei.Backup.Settings\x12%\n\x08tutorial\x18\x05 \x01(\x0b\x32\x13.ei.Backup.Tutorial\x12\x1f\n\x05stats\x18\x06 \x01(\x0b\x32\x10.ei.Backup.Stats\x12\x1d\n\x04game\x18\x07 \x01(\x0b\x32\x0f.ei.Backup.Game\x12\"\n\x03sim\x18\x08 \x01(\x0b\x32\x15.ei.Backup.Simulation\x12$\n\x05\x66\x61rms\x18\x0c \x03(\x0b\x32\x15.ei.Backup.Simulation\x12#\n\x07mission\x18\t \x01(\x0b\x32\x12.ei.Backup.Mission\x12\x1d\n\x04misc\x18\n \x01(\x0b\x32\x0f.ei.Backup.Misc\x12\"\n\tcontracts\x18\r \x01(\x0b\x32\x0f.ei.MyContracts\x12\x10\n\x08\x63hecksum\x18\x64 \x01(\x04\x1a\xc5\x05\n\x08Settings\x12\x0b\n\x03sfx\x18\x01 \x01(\x08\x12\r\n\x05music\x18\x02 \x01(\x08\x12\x18\n\x10low_battery_mode\x18\x03 \x01(\x08\x12\x1c\n\x14low_performance_mode\x18\x04 \x01(\x08\x12\x1f\n\x17\x66orce_touch_chicken_btn\x18\t \x01(\x08\x12\x1d\n\x15notifications_queried\x18\x05 \x01(\x08\x12\x18\n\x10notifications_on\x18\x06 \x01(\x08\x12\x19\n\x11notify_daily_gift\x18\x0b \x01(\x08\x12\x15\n\rcoppa_queried\x18\x07 \x01(\x08\x12\x18\n\x10\x63oppa_restricted\x18\x08 \x01(\x08\x12\x1c\n\x14gdpr_consent_queried\x18\x0c \x01(\x08\x12\x1b\n\x13gdpr_age_restricted\x18\x10 \x01(\x08\x12\x1a\n\x12gdpr_consent_given\x18\r \x01(\x08\x12\x13\n\x0b\x61ge_queried\x18\x12 \x01(\x08\x12\x1d\n\x0e\x61ge_restricted\x18\x13 \x01(\x08:\x05\x66\x61lse\x12\'\n\x1f\x64\x61ta_collection_consent_queried\x18\x14 \x01(\x08\x12%\n\x1d\x64\x61ta_collection_consent_given\x18\x15 \x01(\x08\x12\x1c\n\x14last_day_age_queried\x18\x11 \x01(\r\x12\x1e\n\x10user_ads_enabled\x18\x0e \x01(\x08:\x04true\x12 \n\x12user_cloud_enabled\x18\x0f \x01(\x08:\x04true\x12$\n\x16user_analytics_enabled\x18\x16 \x01(\x08:\x04true\x12+\n\x1duser_personalized_ads_enabled\x18\x17 \x01(\x08:\x04true\x12\x17\n\x0flow_performance\x18\n \x01(\x08\x12\x18\n\x10last_backup_time\x18\x18 \x01(\x01\x1a\x88\x02\n\x08Tutorial\x12\x13\n\x0bintro_shown\x18\x01 \x01(\x08\x12\x1c\n\x14\x63lick_tutorial_shown\x18\x02 \x01(\x08\x12\x15\n\rbuy_hab_shown\x18\x03 \x01(\x08\x12\x1a\n\x12hire_vehicle_shown\x18\x04 \x01(\x08\x12\x13\n\x0bq_num_shown\x18\x05 \x01(\x08\x12\x13\n\x0bs_num_shown\x18\x06 \x01(\x08\x12\x1b\n\x13\x63ontracts_tab_shown\x18\x07 \x01(\x08\x12\x1b\n\x13\x63ontract_info_shown\x18\x08 \x01(\x08\x12\x17\n\x0fjoin_coop_shown\x18\t \x01(\x08\x12\x19\n\x11switch_farm_shown\x18\n \x01(\x08\x1a\xdd\x03\n\x05Stats\x12\x16\n\x0e\x65gg_totals_OLD\x18\x01 \x03(\x04\x12\x12\n\negg_totals\x18\x08 \x03(\x01\x12\x1f\n\x17unlimited_chickens_uses\x18\x07 \x01(\x04\x12\x13\n\x0brefill_uses\x18\x02 \x01(\x04\x12\x13\n\x0bwarp_1_uses\x18\x04 \x01(\x04\x12\x13\n\x0bwarp_8_uses\x18\x05 \x01(\x04\x12\x13\n\x0b\x62oosts_used\x18\r \x01(\x04\x12\x1a\n\x12video_doubler_uses\x18\x03 \x01(\x04\x12\x17\n\x0f\x64rone_takedowns\x18\x06 \x01(\x04\x12\x1d\n\x15\x64rone_takedowns_elite\x18\n \x01(\x04\x12\x15\n\rnum_prestiges\x18\t \x01(\x04\x12\x18\n\x10num_piggy_breaks\x18\x0b \x01(\x04\x12\x1b\n\x13iap_packs_purchased\x18\x0c \x01(\x04\x12\x12\n\npiggy_full\x18\x0e \x01(\x08\x12\x18\n\x10piggy_found_full\x18\x0f \x01(\x08\x12\"\n\x1atime_piggy_filled_realtime\x18\x10 \x01(\x01\x12 \n\x18time_piggy_full_gametime\x18\x11 \x01(\x01\x12\x1d\n\x15lost_piggy_increments\x18\x12 \x01(\x04\x1a\xf5\x08\n\x04Game\x12\x17\n\x0c\x63urrent_farm\x18\x16 \x01(\r:\x01\x30\x12 \n\x0fmax_egg_reached\x18\x01 \x01(\x0e\x32\x07.ei.Egg\x12\x1a\n\x12golden_eggs_earned\x18\x02 \x01(\x04\x12\x19\n\x11golden_eggs_spent\x18\x03 \x01(\x04\x12\x1d\n\x15uncliamed_golden_eggs\x18\x10 \x01(\x04\x12\x11\n\tsoul_eggs\x18\x04 \x01(\x04\x12\x1b\n\x13unclaimed_soul_eggs\x18\x11 \x01(\x04\x12\x13\n\x0bsoul_eggs_d\x18\" \x01(\x01\x12\x1d\n\x15unclaimed_soul_eggs_d\x18# \x01(\x01\x12\x18\n\x10\x65ggs_of_prophecy\x18\x17 \x01(\x04\x12\"\n\x1aunclaimed_eggs_of_prophecy\x18\x18 \x01(\x04\x12\x1c\n\x14prestige_cash_earned\x18\x05 \x01(\x01\x12 \n\x18prestige_soul_boost_cash\x18! \x01(\x01\x12\x1c\n\x14lifetime_cash_earned\x18\x06 \x01(\x01\x12\x12\n\npiggy_bank\x18\x07 \x01(\x04\x12\x1e\n\x16piggy_full_alert_shown\x18\x1f \x01(\x08\x12\x14\n\x0cpermit_level\x18\x08 \x01(\r\x12.\n\repic_research\x18\t \x03(\x0b\x32\x17.ei.Backup.ResearchItem\x12\x19\n\x11hyperloop_station\x18\x1d \x01(\x08\x12\x1c\n\x14next_daily_gift_time\x18\n \x01(\x01\x12%\n\x1dlast_daily_gift_collected_day\x18\x14 \x01(\r\x12!\n\x19num_daily_gifts_collected\x18\x1c \x01(\r\x12%\n\x04news\x18\x0b \x03(\x0b\x32\x17.ei.Backup.NewsHeadline\x12\x16\n\x0elast_news_time\x18\x0c \x01(\x01\x12\x1a\n\x12\x63urrent_multiplier\x18\r \x01(\x01\x12%\n\x1d\x63urrent_multiplier_expiration\x18\x0e \x01(\x01\x12\x30\n\x0c\x61\x63hievements\x18\x0f \x03(\x0b\x32\x1a.ei.Backup.AchievementInfo\x12\x1d\n\x15max_farm_size_reached\x18\x12 \x03(\x04\x12\x17\n\x0f\x65gg_medal_level\x18\x13 \x03(\r\x12\"\n\x1along_idle_notification_set\x18\x19 \x01(\x08\x12(\n long_idle_notification_threshold\x18\x1a \x01(\x01\x12\x18\n\x10long_idle_reward\x18\x1b \x01(\x01\x12%\n\x06\x62oosts\x18\x1e \x03(\x0b\x32\x15.ei.Backup.OwnedBoost\x12\"\n\x1atotal_time_cheats_detected\x18  \x01(\r\x12\x1d\n\x15\x66orce_elite_contracts\x18$ \x01(\x08\x12!\n\x19new_player_event_end_time\x18% \x01(\x01\x1a\xaf\x06\n\nSimulation\x12\x19\n\x08\x65gg_type\x18\x01 \x01(\x0e\x32\x07.ei.Egg\x12\x1f\n\tfarm_type\x18\x13 \x01(\x0e\x32\x0c.ei.FarmType\x12\x13\n\x0b\x63ontract_id\x18\x14 \x01(\t\x12\x13\n\x0b\x63\x61sh_earned\x18\x02 \x01(\x01\x12\x12\n\ncash_spent\x18\x03 \x01(\x01\x12\x16\n\x0eunclaimed_cash\x18\x04 \x01(\x01\x12\x16\n\x0elast_step_time\x18\x05 \x01(\x01\x12\x14\n\x0cnum_chickens\x18\x06 \x01(\x04\x12\x1e\n\x16num_chickens_unsettled\x18\x07 \x01(\x04\x12\x1c\n\x14num_chickens_running\x18\x08 \x01(\x04\x12\x11\n\teggs_laid\x18\t \x01(\x01\x12\x15\n\reggs_paid_for\x18\n \x01(\x01\x12\x13\n\x0bsilos_owned\x18\x0b \x01(\r\x12\x0c\n\x04habs\x18\x0c \x03(\r\x12\x16\n\x0ehab_population\x18\r \x03(\x04\x12\x1e\n\x16hab_population_indound\x18\x0e \x03(\x04\x12!\n\x19hab_incubator_popuplation\x18\x0f \x03(\x01\x12\x1b\n\x13hatchery_population\x18\x10 \x01(\x01\x12\x10\n\x08vehicles\x18\x11 \x03(\r\x12\x14\n\x0ctrain_length\x18\x15 \x03(\r\x12\x30\n\x0f\x63ommon_research\x18\x12 \x03(\x0b\x32\x17.ei.Backup.ResearchItem\x12-\n\ractive_boosts\x18\x16 \x03(\x0b\x32\x16.ei.Backup.ActiveBoost\x12\x1c\n\x14time_cheats_detected\x18\x17 \x01(\r\x12\x17\n\x0ftime_cheat_debt\x18\x18 \x01(\x01\x12\x1d\n\x15\x62oost_tokens_received\x18\x19 \x01(\r\x12\x1a\n\x12\x62oost_tokens_spent\x18\x1a \x01(\r\x12\x1a\n\x12\x62oost_tokens_given\x18\x1c \x01(\r\x12\x1e\n\x16unclaimed_boost_tokens\x18\x1b \x01(\r\x12\'\n\x1fgametime_until_next_boost_token\x18\x1d \x01(\x01\x1a\x7f\n\x07Mission\x12\x17\n\x0f\x63urrent_mission\x18\x01 \x01(\t\x12\x17\n\x0freference_value\x18\x02 \x01(\x01\x12\x18\n\x10\x63urrent_missions\x18\x04 \x03(\t\x12(\n\x08missions\x18\x03 \x03(\x0b\x32\x16.ei.Backup.MissionInfo\x1a\xa7\x04\n\x04Misc\x12\x1c\n\x14\x63hicken_btn_pref_big\x18\x01 \x01(\x08\x12\"\n\x1a\x66ree_hatchery_refill_given\x18\x02 \x01(\x08\x12\'\n\x15last_share_farm_value\x18\x03 \x01(\x01:\x08\x31\x30\x30\x30\x30\x30\x30\x30\x12-\n\x1blast_share_swarm_farm_value\x18\x04 \x01(\x01:\x08\x31\x30\x30\x30\x30\x30\x30\x30\x12\"\n\x15last_share_swarm_size\x18\x05 \x01(\x01:\x03\x31\x34\x30\x12)\n\x1dlast_prestige_alert_soul_eggs\x18\n \x01(\x04:\x02\x34\x35\x12\x13\n\x0b\x66riend_rank\x18\x06 \x01(\x04\x12\x17\n\x0f\x66riend_rank_pop\x18\x07 \x01(\x04\x12\x13\n\x0bglobal_rank\x18\x08 \x01(\x04\x12\x17\n\x0fglobal_rank_pop\x18\t \x01(\x04\x12\x14\n\x0ctrophy_alert\x18\x0b \x01(\x08\x12\x10\n\x08\x61r_alert\x18\x0c \x01(\x08\x12\x17\n\x0f\x63ontracts_alert\x18\r \x01(\x08\x12\x12\n\ncoop_alert\x18\x0e \x01(\x08\x12\x14\n\x0cswitch_alert\x18\x0f \x01(\x08\x12\x1d\n\x15\x65gg_of_prophecy_alert\x18\x10 \x01(\x08\x12\x19\n\x11\x62oost_token_alert\x18\x11 \x01(\x08\x12\x16\n\x0esoul_egg_alert\x18\x12 \x01(\x08\x12\x1d\n\x15\x62\x61\x63kup_reminder_alert\x18\x13 \x01(\x08\x1a)\n\x0cResearchItem\x12\n\n\x02id\x18\x01 \x01(\t\x12\r\n\x05level\x18\x02 \x01(\r\x1a(\n\x0cNewsHeadline\x12\n\n\x02id\x18\x01 \x01(\t\x12\x0c\n\x04read\x18\x02 \x01(\x08\x1a/\n\x0f\x41\x63hievementInfo\x12\n\n\x02id\x18\x01 \x01(\t\x12\x10\n\x08\x61\x63hieved\x18\x02 \x01(\x08\x1aP\n\x0b\x41\x63tiveBoost\x12\x10\n\x08\x62oost_id\x18\x01 \x01(\t\x12\x16\n\x0etime_remaining\x18\x02 \x01(\x01\x12\x17\n\x0freference_value\x18\x03 \x01(\x01\x1a-\n\nOwnedBoost\x12\x10\n\x08\x62oost_id\x18\x01 \x01(\t\x12\r\n\x05\x63ount\x18\x02 \x01(\r\x1a\x45\n\x0bMissionInfo\x12\n\n\x02id\x18\x01 \x01(\t\x12\x11\n\tcompleted\x18\x02 \x01(\x08\x12\x17\n\x0freference_value\x18\x03 \x01(\x01\"d\n\x19\x45ggIncFirstContactRequest\x12\x0f\n\x07user_id\x18\x01 \x01(\t\x12\x16\n\x0e\x63lient_version\x18\x02 \x01(\r\x12\x1e\n\x08platform\x18\x03 \x01(\x0e\x32\x0c.ei.Platform\"8\n\x1a\x45ggIncFirstContactResponse\x12\x1a\n\x06\x62\x61\x63kup\x18\x01 \x01(\x0b\x32\n.ei.Backup\"9\n\x0e\x45ggIncAdConfig\x12\'\n\x10network_priority\x18\x01 \x03(\x0e\x32\r.ei.AdNetwork\"A\n\rDailyGiftInfo\x12\x13\n\x0b\x63urrent_day\x18\x01 \x01(\r\x12\x1b\n\x13seconds_to_next_day\x18\x02 \x01(\x01\"\xc8\x01\n\x10SalesInfoRequest\x12\x0f\n\x07user_id\x18\x01 \x01(\t\x12\x12\n\npiggy_full\x18\x02 \x01(\x08\x12\x18\n\x10piggy_found_full\x18\x03 \x01(\x08\x12\x1d\n\x15seconds_full_realtime\x18\x04 \x01(\x01\x12\x1d\n\x15seconds_full_gametime\x18\x05 \x01(\x01\x12\x17\n\x0flost_increments\x18\x07 \x01(\r\x12\x1e\n\x16\x63urrent_client_version\x18\n \x01(\r\"g\n\x0cIAPSaleEntry\x12\x12\n\nproduct_id\x18\x01 \x01(\t\x12\x19\n\x11seconds_remaining\x18\x02 \x01(\x01\x12\x17\n\x0f\x64iscount_string\x18\x03 \x01(\t\x12\x0f\n\x07sale_id\x18\x04 \x01(\t\",\n\tSalesInfo\x12\x1f\n\x05sales\x18\x01 \x03(\x0b\x32\x10.ei.IAPSaleEntry\"p\n\x0b\x45ggIncEvent\x12\x12\n\nidentifier\x18\x01 \x01(\t\x12\x19\n\x11seconds_remaining\x18\x02 \x01(\x01\x12\x0c\n\x04type\x18\x03 \x01(\t\x12\x12\n\nmultiplier\x18\x04 \x01(\x01\x12\x10\n\x08subtitle\x18\x05 \x01(\t\"6\n\x13\x45ggIncCurrentEvents\x12\x1f\n\x06\x65vents\x18\x01 \x03(\x0b\x32\x0f.ei.EggIncEvent\"\xa9\x02\n\nDeviceInfo\x12\x11\n\tdevice_id\x18\x01 \x01(\t\x12\x16\n\x0e\x61\x64vertising_id\x18\r \x01(\t\x12\x10\n\x08platform\x18\x02 \x01(\t\x12\x13\n\x0b\x66orm_factor\x18\x03 \x01(\t\x12\x13\n\x0b\x64\x65vice_name\x18\x04 \x01(\t\x12\x18\n\x10platform_version\x18\x05 \x01(\t\x12\x16\n\x0elocale_country\x18\x06 \x01(\t\x12\x17\n\x0flocale_language\x18\x07 \x01(\t\x12\x12\n\ngpu_vendor\x18\x08 \x01(\t\x12\x11\n\tgpu_model\x18\t \x01(\t\x12\x15\n\rdevice_bucket\x18\n \x01(\t\x12\x14\n\x0cscreen_width\x18\x0b \x01(\r\x12\x15\n\rscreen_height\x18\x0c \x01(\r\"\xd5\x05\n\x07\x41ppInfo\x12\x13\n\x0bversion_str\x18\x01 \x01(\t\x12\x10\n\x08sessions\x18\x02 \x01(\r\x12\x15\n\rnum_prestiges\x18\x03 \x01(\r\x12\x11\n\tsoul_eggs\x18\x04 \x01(\x04\x12\x13\n\x0b\x63urrent_egg\x18\x05 \x01(\r\x12\x13\n\x0bgold_earned\x18\x06 \x01(\x04\x12\x12\n\ngold_spent\x18\x07 \x01(\x04\x12\x17\n\x0f\x63urrent_mission\x18\x08 \x01(\r\x12\x12\n\npiggy_size\x18\t \x01(\r\x12\x18\n\x10num_piggy_breaks\x18\n \x01(\x04\x12\x1d\n\x15verified_piggy_breaks\x18\x1d \x01(\x04\x12\x1b\n\x13iap_packs_purchased\x18\x0b \x01(\x04\x12\x14\n\x0cpermit_level\x18\x0c \x01(\r\x12\x1a\n\x12video_doubler_uses\x18\x0f \x01(\x04\x12\x17\n\x0f\x64rone_takedowns\x18\x12 \x01(\x04\x12\x1d\n\x15\x64rone_takedowns_elite\x18\x13 \x01(\x04\x12\x19\n\x11trophies_unlocked\x18\x14 \x01(\x08\x12\x11\n\tegg_level\x18\x15 \x01(\x02\x12\x17\n\x0fstruggle_factor\x18\x16 \x01(\x02\x12\x12\n\npiggy_full\x18\x17 \x01(\x08\x12\x18\n\x10piggy_found_full\x18\x18 \x01(\x08\x12 \n\x18time_piggy_full_realtime\x18\x19 \x01(\x01\x12 \n\x18time_piggy_full_gametime\x18\x1a \x01(\x01\x12\x1d\n\x15lost_piggy_increments\x18\x1b \x01(\x04\x12\x0f\n\x07sale_id\x18\x1c \x01(\t\x12\x1f\n\x17unlimited_chickens_uses\x18\r \x01(\x04\x12\x13\n\x0brefill_uses\x18\x0e \x01(\x04\x12\x17\n\x0fshort_warp_uses\x18\x10 \x01(\x04\x12\x16\n\x0elong_warp_uses\x18\x11 \x01(\x04\"0\n\x12\x41\x63tionKeyValuePair\x12\x0b\n\x03key\x18\x01 \x02(\t\x12\r\n\x05value\x18\x02 \x02(\t\"\xc2\x01\n\rGenericAction\x12\x0f\n\x07user_id\x18\x01 \x01(\t\x12\x16\n\x0e\x61\x64vertising_id\x18\x07 \x01(\t\x12\x13\n\x0b\x61pprox_time\x18\x02 \x01(\x02\x12\x13\n\x0b\x61\x63tion_name\x18\x03 \x01(\t\x12$\n\x04\x64\x61ta\x18\x04 \x03(\x0b\x32\x16.ei.ActionKeyValuePair\x12\x18\n\x03\x61pp\x18\x05 \x01(\x0b\x32\x0b.ei.AppInfo\x12\x1e\n\x06\x64\x65vice\x18\x06 \x01(\x0b\x32\x0e.ei.DeviceInfo\"\x7f\n\x15VerifyPurchaseRequest\x12\x0b\n\x03sku\x18\x01 \x01(\t\x12\x16\n\x0etransaction_id\x18\x02 \x01(\t\x12\x0f\n\x07receipt\x18\x03 \x01(\t\x12\x10\n\x08platform\x18\x04 \x01(\t\x12\x1e\n\x03log\x18\x05 \x01(\x0b\x32\x11.ei.GenericAction\";\n\x16VerifyPurchaseResponse\x12\x10\n\x08verified\x18\x01 \x01(\x08\x12\x0f\n\x07message\x18\x02 \x01(\t\"\xcd\x04\n\x08\x43ontract\x12\x12\n\nidentifier\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\t \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\n \x01(\t\x12\x14\n\x03\x65gg\x18\x02 \x01(\x0e\x32\x07.ei.Egg\x12 \n\x05goals\x18\x03 \x03(\x0b\x32\x11.ei.Contract.Goal\x12\'\n\tgoal_sets\x18\x10 \x03(\x0b\x32\x14.ei.Contract.GoalSet\x12\x14\n\x0c\x63oop_allowed\x18\x04 \x01(\x08\x12\x15\n\rmax_coop_size\x18\x05 \x01(\r\x12\x12\n\nmax_boosts\x18\x0c \x01(\r\x12\x1d\n\x11minutes_per_token\x18\x0f \x01(\x01:\x02\x36\x30\x12\x17\n\x0f\x65xpiration_time\x18\x06 \x01(\x01\x12\x16\n\x0elength_seconds\x18\x07 \x01(\x01\x12\x15\n\rmax_soul_eggs\x18\r \x01(\x01\x12\x1a\n\x12min_client_version\x18\x0e \x01(\r\x12\r\n\x05\x64\x65\x62ug\x18\x0b \x01(\x08\x1a\xa8\x01\n\x04Goal\x12\x1a\n\x04type\x18\x01 \x01(\x0e\x32\x0c.ei.GoalType\x12\x15\n\rtarget_amount\x18\x02 \x01(\x01\x12#\n\x0breward_type\x18\x03 \x01(\x0e\x32\x0e.ei.RewardType\x12\x17\n\x0freward_sub_type\x18\x04 \x01(\t\x12\x15\n\rreward_amount\x18\x05 \x01(\x01\x12\x18\n\x10target_soul_eggs\x18\x06 \x01(\x01\x1a+\n\x07GoalSet\x12 \n\x05goals\x18\x01 \x03(\x0b\x32\x11.ei.Contract.Goal\"=\n\x10\x43ontractsRequest\x12\x11\n\tsoul_eggs\x18\x01 \x01(\x01\x12\x16\n\x0e\x63lient_version\x18\x05 \x01(\r\"y\n\x11\x43ontractsResponse\x12\x1f\n\tcontracts\x18\x01 \x03(\x0b\x32\x0c.ei.Contract\x12\x17\n\x0fwarning_message\x18\x04 \x01(\t\x12\x13\n\x0bserver_time\x18\x02 \x01(\x01\x12\x15\n\x07max_eop\x18\x03 \x01(\r:\x04\x31\x30\x30\x30\"b\n\x19\x43ontractCoopStatusRequest\x12\x1b\n\x13\x63ontract_identifier\x18\x01 \x01(\t\x12\x17\n\x0f\x63oop_identifier\x18\x02 \x01(\t\x12\x0f\n\x07user_id\x18\x03 \x01(\t\"\xe6\x05\n\x1a\x43ontractCoopStatusResponse\x12\x1b\n\x13\x63ontract_identifier\x18\x01 \x01(\t\x12\x14\n\x0ctotal_amount\x18\x02 \x01(\x01\x12\x17\n\x0f\x63oop_identifier\x18\x03 \x01(\t\x12\x45\n\x0c\x63ontributors\x18\x04 \x03(\x0b\x32/.ei.ContractCoopStatusResponse.ContributionInfo\x12\x16\n\x0e\x61uto_generated\x18\x08 \x01(\x08\x12\x0e\n\x06public\x18\n \x01(\x08\x12\x12\n\ncreator_id\x18\t \x01(\t\x12\x19\n\x11seconds_remaining\x18\x05 \x01(\x01\x12\x1d\n\x15\x61ll_members_reporting\x18\x06 \x01(\x08\x12&\n\x1egrace_period_seconds_remaining\x18\x07 \x01(\x01\x12\x36\n\x05gifts\x18\x0b \x03(\x0b\x32\'.ei.ContractCoopStatusResponse.CoopGift\x1a\x9e\x02\n\x10\x43ontributionInfo\x12\x0f\n\x07user_id\x18\x01 \x01(\t\x12\x11\n\tuser_name\x18\x02 \x01(\t\x12\x1b\n\x13\x63ontribution_amount\x18\x03 \x01(\x01\x12\x19\n\x11\x63ontribution_rate\x18\x06 \x01(\x01\x12\x12\n\nsoul_power\x18\x0b \x01(\x01\x12\x13\n\x0brank_change\x18\x08 \x01(\x05\x12\x0e\n\x06\x61\x63tive\x18\x04 \x01(\x08\x12\x1b\n\x13time_cheat_detected\x18\x07 \x01(\x08\x12\x1e\n\x08platform\x18\x05 \x01(\x0e\x32\x0c.ei.Platform\x12\x0f\n\x07push_id\x18\t \x01(\t\x12\x11\n\tban_votes\x18\n \x01(\r\x12\x14\n\x0c\x62oost_tokens\x18\x0c \x01(\r\x1a>\n\x08\x43oopGift\x12\x0f\n\x07user_id\x18\x01 \x01(\t\x12\x11\n\tuser_name\x18\x03 \x01(\t\x12\x0e\n\x06\x61mount\x18\x02 \x01(\r\"\x9f\x03\n\rLocalContract\x12\x1e\n\x08\x63ontract\x18\x01 \x01(\x0b\x32\x0c.ei.Contract\x12\x17\n\x0f\x63oop_identifier\x18\x02 \x01(\t\x12\x10\n\x08\x61\x63\x63\x65pted\x18\x07 \x01(\x08\x12\x15\n\rtime_accepted\x18\x03 \x01(\x01\x12\x11\n\tcancelled\x18\x04 \x01(\x08\x12\x0b\n\x03new\x18\x08 \x01(\x08\x12\x1c\n\x14\x63oop_shared_end_time\x18\x05 \x01(\x01\x12\"\n\x1a\x63oop_grace_period_end_time\x18\t \x01(\x01\x12#\n\x1b\x63oop_contribution_finalized\x18\n \x01(\x08\x12\'\n\x1f\x63oop_last_uploaded_contribution\x18\x0b \x01(\x01\x12\x14\n\x0c\x63oop_user_id\x18\r \x01(\t\x12%\n\x1dlast_amount_when_reward_given\x18\x06 \x01(\x01\x12\x1a\n\x12num_goals_achieved\x18\x0e \x01(\r\x12\x13\n\x0b\x62oosts_used\x18\x0c \x01(\r\x12\x0e\n\x06league\x18\x0f \x01(\r\"\xb1\x01\n\x0bMyContracts\x12\x19\n\x11\x63ontract_ids_seen\x18\x03 \x03(\t\x12$\n\tcontracts\x18\x01 \x03(\x0b\x32\x11.ei.LocalContract\x12\"\n\x07\x61rchive\x18\x02 \x03(\x0b\x32\x11.ei.LocalContract\x12=\n\x15\x63urrent_coop_statuses\x18\x04 \x03(\x0b\x32\x1e.ei.ContractCoopStatusResponse\"p\n\x10QueryCoopRequest\x12\x1b\n\x13\x63ontract_identifier\x18\x01 \x01(\t\x12\x17\n\x0f\x63oop_identifier\x18\x02 \x01(\t\x12\x0e\n\x06league\x18\x04 \x01(\r\x12\x16\n\x0e\x63lient_version\x18\x03 \x01(\r\"[\n\x11QueryCoopResponse\x12\x0e\n\x06\x65xists\x18\x01 \x01(\x08\x12\x0c\n\x04\x66ull\x18\x02 \x01(\x08\x12\x18\n\x10\x64ifferent_league\x18\x04 \x01(\x08\x12\x0e\n\x06\x62\x61nned\x18\x03 \x01(\x08\"\xe4\x01\n\x11\x43reateCoopRequest\x12\x1b\n\x13\x63ontract_identifier\x18\x01 \x01(\t\x12\x17\n\x0f\x63oop_identifier\x18\x02 \x01(\t\x12\x19\n\x11seconds_remaining\x18\x03 \x01(\x01\x12\x0f\n\x07user_id\x18\x04 \x01(\t\x12\x11\n\tuser_name\x18\x05 \x01(\t\x12\x12\n\nsoul_power\x18\x08 \x01(\x01\x12\x0e\n\x06league\x18\t \x01(\r\x12\x1e\n\x08platform\x18\x06 \x01(\x0e\x32\x0c.ei.Platform\x12\x16\n\x0e\x63lient_version\x18\x07 \x01(\r\"6\n\x12\x43reateCoopResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x0f\n\x07message\x18\x02 \x01(\t\"\xc7\x01\n\x0fJoinCoopRequest\x12\x1b\n\x13\x63ontract_identifier\x18\x01 \x01(\t\x12\x17\n\x0f\x63oop_identifier\x18\x02 \x01(\t\x12\x0f\n\x07user_id\x18\x03 \x01(\t\x12\x11\n\tuser_name\x18\x04 \x01(\t\x12\x12\n\nsoul_power\x18\x08 \x01(\x01\x12\x0e\n\x06league\x18\t \x01(\r\x12\x1e\n\x08platform\x18\x05 \x01(\x0e\x32\x0c.ei.Platform\x12\x16\n\x0e\x63lient_version\x18\x07 \x01(\r\"x\n\x10JoinCoopResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x0f\n\x07message\x18\x02 \x01(\t\x12\x0e\n\x06\x62\x61nned\x18\x04 \x01(\x08\x12\x17\n\x0f\x63oop_identifier\x18\x05 \x01(\t\x12\x19\n\x11seconds_remaining\x18\x03 \x01(\x01\"\xcd\x01\n\x13\x41utoJoinCoopRequest\x12\x1b\n\x13\x63ontract_identifier\x18\x01 \x01(\t\x12\x0f\n\x07user_id\x18\x02 \x01(\t\x12\x11\n\tuser_name\x18\x03 \x01(\t\x12\x12\n\nsoul_power\x18\x04 \x01(\x01\x12\x0e\n\x06league\x18\x08 \x01(\r\x12\x19\n\x11seconds_remaining\x18\x05 \x01(\x01\x12\x1e\n\x08platform\x18\x06 \x01(\x0e\x32\x0c.ei.Platform\x12\x16\n\x0e\x63lient_version\x18\x07 \x01(\r\"\x98\x01\n\x1cUpdateCoopPermissionsRequest\x12\x1b\n\x13\x63ontract_identifier\x18\x01 \x01(\t\x12\x17\n\x0f\x63oop_identifier\x18\x02 \x01(\t\x12\x1a\n\x12requesting_user_id\x18\x03 \x01(\t\x12\x0e\n\x06public\x18\x04 \x01(\x08\x12\x16\n\x0e\x63lient_version\x18\x05 \x01(\r\"A\n\x1dUpdateCoopPermissionsResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x0f\n\x07message\x18\x02 \x01(\t\"{\n\x10LeaveCoopRequest\x12\x1b\n\x13\x63ontract_identifier\x18\x01 \x01(\t\x12\x17\n\x0f\x63oop_identifier\x18\x02 \x01(\t\x12\x19\n\x11player_identifier\x18\x03 \x01(\t\x12\x16\n\x0e\x63lient_version\x18\x07 \x01(\r\"\xca\x01\n\x15GiftPlayerCoopRequest\x12\x1b\n\x13\x63ontract_identifier\x18\x01 \x01(\t\x12\x17\n\x0f\x63oop_identifier\x18\x02 \x01(\t\x12\x19\n\x11player_identifier\x18\x03 \x01(\t\x12\x1a\n\x12requesting_user_id\x18\x04 \x01(\t\x12\x1c\n\x14requesting_user_name\x18\x06 \x01(\t\x12\x0e\n\x06\x61mount\x18\x05 \x01(\r\x12\x16\n\x0e\x63lient_version\x18\x07 \x01(\r\"\x9c\x01\n\x15KickPlayerCoopRequest\x12\x1b\n\x13\x63ontract_identifier\x18\x01 \x01(\t\x12\x17\n\x0f\x63oop_identifier\x18\x02 \x01(\t\x12\x19\n\x11player_identifier\x18\x03 \x01(\t\x12\x1a\n\x12requesting_user_id\x18\x04 \x01(\t\x12\x16\n\x0e\x63lient_version\x18\x07 \x01(\r\"\xe4\x01\n\x1f\x43ontractCoopStatusUpdateRequest\x12\x0f\n\x07user_id\x18\x01 \x01(\t\x12\x1b\n\x13\x63ontract_identifier\x18\x02 \x01(\t\x12\x17\n\x0f\x63oop_identifier\x18\x03 \x01(\t\x12\x14\n\x0cpush_user_id\x18\x08 \x01(\t\x12\x0e\n\x06\x61mount\x18\x04 \x01(\x01\x12\x0c\n\x04rate\x18\x05 \x01(\x01\x12\x1c\n\x14time_cheats_detected\x18\x06 \x01(\r\x12\x12\n\nsoul_power\x18\x07 \x01(\x01\x12\x14\n\x0c\x62oost_tokens\x18\t \x01(\r\"5\n ContractCoopStatusUpdateResponse\x12\x11\n\tfinalized\x18\x01 \x01(\x08\"R\n\x13UserDataInfoRequest\x12\x0f\n\x07user_id\x18\x01 \x01(\t\x12\x11\n\tdevice_id\x18\x02 \x01(\t\x12\x17\n\x0f\x62\x61\x63kup_checksum\x18\x03 \x01(\x04\"d\n\x14UserDataInfoResponse\x12\x17\n\x0f\x62\x61\x63kup_checksum\x18\x01 \x01(\x04\x12\x19\n\x11\x62\x61\x63kup_total_cash\x18\x02 \x01(\x01\x12\x18\n\x10\x63oop_memberships\x18\x03 \x03(\t\"V\n\x17\x43learAllUserDataRequest\x12\x0f\n\x07user_id\x18\x01 \x01(\t\x12\x11\n\tdevice_id\x18\x02 \x01(\t\x12\x17\n\x0f\x62\x61\x63kup_checksum\x18\x03 \x01(\x04\"r\n\nServerGift\x12\x0f\n\x07user_id\x18\x01 \x01(\t\x12#\n\x0breward_type\x18\x03 \x01(\x0e\x32\x0e.ei.RewardType\x12\x17\n\x0freward_sub_type\x18\x04 \x01(\t\x12\x15\n\rreward_amount\x18\x05 \x01(\x01\"\xa5\x01\n\x13PeriodicalsResponse\x12\x1c\n\x05sales\x18\x01 \x01(\x0b\x32\r.ei.SalesInfo\x12\'\n\x06\x65vents\x18\x02 \x01(\x0b\x32\x17.ei.EggIncCurrentEvents\x12(\n\tcontracts\x18\x03 \x01(\x0b\x32\x15.ei.ContractsResponse\x12\x1d\n\x05gifts\x18\x04 \x03(\x0b\x32\x0e.ei.ServerGift\"\xef\x01\n\x15GetPeriodicalsRequest\x12\x0f\n\x07user_id\x18\x01 \x01(\t\x12\x12\n\npiggy_full\x18\x02 \x01(\x08\x12\x18\n\x10piggy_found_full\x18\x03 \x01(\x08\x12\x1d\n\x15seconds_full_realtime\x18\x04 \x01(\x01\x12\x1d\n\x15seconds_full_gametime\x18\x05 \x01(\x01\x12\x17\n\x0flost_increments\x18\x07 \x01(\r\x12\x11\n\tsoul_eggs\x18\x08 \x01(\x01\x12\x1e\n\x16\x63urrent_client_version\x18\n \x01(\r\x12\r\n\x05\x64\x65\x62ug\x18\x0b \x01(\x08\"d\n\x14\x41\x64\x41ttributionRawData\x12\x14\n\x0c\x64\x65vice_ad_id\x18\x01 \x01(\t\x12\x0f\n\x07user_id\x18\x04 \x01(\t\x12\x12\n\nad_network\x18\x02 \x01(\t\x12\x11\n\tjson_data\x18\x03 \x01(\t\"\xb8\x01\n\x10\x41\x64\x41ttributionRow\x12\x0f\n\x07user_id\x18\x01 \x01(\t\x12\r\n\x05\x61\x64_id\x18\x02 \x01(\t\x12\x12\n\nad_network\x18\x03 \x01(\t\x12\x10\n\x08\x63\x61mpaign\x18\x04 \x01(\t\x12\x0f\n\x07keyword\x18\x05 \x01(\t\x12\r\n\x05\x65xtra\x18\x06 \x01(\t\x12\x12\n\nclick_date\x18\x07 \x01(\x02\x12\x15\n\rdownload_date\x18\x08 \x01(\x02\x12\x13\n\x0b\x61pprox_time\x18\t \x01(\x02\"\x8d\x03\n\x11\x41\x64\x41ttributionInfo\x12\x14\n\x0c\x64\x65vice_ad_id\x18\x01 \x01(\t\x12\x14\n\x0cnetwork_name\x18\x02 \x01(\t\x12\x13\n\x0b\x61ttribution\x18\x03 \x01(\x08\x12\x10\n\x08org_name\x18\x04 \x01(\t\x12\x0e\n\x06org_id\x18\x05 \x01(\t\x12\x15\n\rcampaign_name\x18\x06 \x01(\t\x12\x13\n\x0b\x63\x61mpaign_id\x18\x07 \x01(\t\x12\x12\n\nclick_date\x18\x08 \x01(\t\x12\x17\n\x0f\x63onversion_date\x18\t \x01(\t\x12\x17\n\x0f\x63onversion_type\x18\n \x01(\t\x12\x0b\n\x03geo\x18\x0b \x01(\t\x12\x14\n\x0c\x61\x64group_name\x18\x0c \x01(\t\x12\x12\n\nadgroup_id\x18\r \x01(\t\x12\x0f\n\x07keyword\x18\x0e \x01(\t\x12\x12\n\nkeyword_id\x18\x0f \x01(\t\x12\x15\n\rkeyword_extra\x18\x10 \x01(\t\x12\x18\n\x10\x63reativeset_name\x18\x11 \x01(\t\x12\x16\n\x0e\x63reativeset_id\x18\x12 \x01(\t*\x1e\n\x08Platform\x12\x07\n\x03IOS\x10\x01\x12\t\n\x05\x44ROID\x10\x02*)\n\x10\x44\x65viceFormFactor\x12\t\n\x05PHONE\x10\x01\x12\n\n\x06TABLET\x10\x02*k\n\tAdNetwork\x12\n\n\x06VUNGLE\x10\x00\x12\x0e\n\nCHARTBOOST\x10\x01\x12\r\n\tAD_COLONY\x10\x02\x12\x0c\n\x08HYPER_MX\x10\x03\x12\t\n\x05UNITY\x10\x04\x12\x0c\n\x08\x46\x41\x43\x45\x42OOK\x10\x05\x12\x0c\n\x08\x41PPLOVIN\x10\x06*\xee\x02\n\x03\x45gg\x12\n\n\x06\x45\x44IBLE\x10\x01\x12\r\n\tSUPERFOOD\x10\x02\x12\x0b\n\x07MEDICAL\x10\x03\x12\x0f\n\x0bROCKET_FUEL\x10\x04\x12\x12\n\x0eSUPER_MATERIAL\x10\x05\x12\n\n\x06\x46USION\x10\x06\x12\x0b\n\x07QUANTUM\x10\x07\x12\x0f\n\x0bIMMORTALITY\x10\x08\x12\x0b\n\x07TACHYON\x10\t\x12\x0c\n\x08GRAVITON\x10\n\x12\r\n\tDILITHIUM\x10\x0b\x12\x0b\n\x07PRODIGY\x10\x0c\x12\r\n\tTERRAFORM\x10\r\x12\x0e\n\nANTIMATTER\x10\x0e\x12\x0f\n\x0b\x44\x41RK_MATTER\x10\x0f\x12\x06\n\x02\x41I\x10\x10\x12\n\n\x06NEBULA\x10\x11\x12\x0c\n\x08UNIVERSE\x10\x12\x12\x11\n\rENLIGHTENMENT\x10\x13\x12\r\n\tCHOCOLATE\x10\x64\x12\n\n\x06\x45\x41STER\x10\x65\x12\x10\n\x0cWATERBALLOON\x10\x66\x12\x0c\n\x08\x46IREWORK\x10g\x12\x0b\n\x07PUMPKIN\x10h\x12\x0c\n\x07UNKNOWN\x10\xe8\x07*-\n\x08\x46\x61rmType\x12\t\n\x05\x45MPTY\x10\x01\x12\x08\n\x04HOME\x10\x02\x12\x0c\n\x08\x43ONTRACT\x10\x03*+\n\x08GoalType\x12\r\n\tEGGS_LAID\x10\x01\x12\x10\n\x0cUNKNOWN_GOAL\x10\x64*\xc9\x01\n\nRewardType\x12\x08\n\x04\x43\x41SH\x10\x01\x12\x08\n\x04GOLD\x10\x02\x12\r\n\tSOUL_EGGS\x10\x03\x12\x14\n\x10\x45GGS_OF_PROPHECY\x10\x04\x12\x16\n\x12\x45PIC_RESEARCH_ITEM\x10\x05\x12\x0e\n\nPIGGY_FILL\x10\x06\x12\x14\n\x10PIGGY_MULTIPLIER\x10\x07\x12\x14\n\x10PIGGY_LEVEL_BUMP\x10\x08\x12\t\n\x05\x42OOST\x10\t\x12\x0f\n\x0b\x42OOST_TOKEN\x10\n\x12\x12\n\x0eUNKNOWN_REWARD\x10\x64')
+
+_globals = globals()
+_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
+_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'ei_pb2', _globals)
+if _descriptor._USE_C_DESCRIPTORS == False:
+  DESCRIPTOR._options = None
+  _globals['_PLATFORM']._serialized_start=12938
+  _globals['_PLATFORM']._serialized_end=12968
+  _globals['_DEVICEFORMFACTOR']._serialized_start=12970
+  _globals['_DEVICEFORMFACTOR']._serialized_end=13011
+  _globals['_ADNETWORK']._serialized_start=13013
+  _globals['_ADNETWORK']._serialized_end=13120
+  _globals['_EGG']._serialized_start=13123
+  _globals['_EGG']._serialized_end=13489
+  _globals['_FARMTYPE']._serialized_start=13491
+  _globals['_FARMTYPE']._serialized_end=13536
+  _globals['_GOALTYPE']._serialized_start=13538
+  _globals['_GOALTYPE']._serialized_end=13581
+  _globals['_REWARDTYPE']._serialized_start=13584
+  _globals['_REWARDTYPE']._serialized_end=13785
+  _globals['_BACKUP']._serialized_start=17
+  _globals['_BACKUP']._serialized_end=4928
+  _globals['_BACKUP_SETTINGS']._serialized_start=493
+  _globals['_BACKUP_SETTINGS']._serialized_end=1202
+  _globals['_BACKUP_TUTORIAL']._serialized_start=1205
+  _globals['_BACKUP_TUTORIAL']._serialized_end=1469
+  _globals['_BACKUP_STATS']._serialized_start=1472
+  _globals['_BACKUP_STATS']._serialized_end=1949
+  _globals['_BACKUP_GAME']._serialized_start=1952
+  _globals['_BACKUP_GAME']._serialized_end=3093
+  _globals['_BACKUP_SIMULATION']._serialized_start=3096
+  _globals['_BACKUP_SIMULATION']._serialized_end=3911
+  _globals['_BACKUP_MISSION']._serialized_start=3913
+  _globals['_BACKUP_MISSION']._serialized_end=4040
+  _globals['_BACKUP_MISC']._serialized_start=4043
+  _globals['_BACKUP_MISC']._serialized_end=4594
+  _globals['_BACKUP_RESEARCHITEM']._serialized_start=4596
+  _globals['_BACKUP_RESEARCHITEM']._serialized_end=4637
+  _globals['_BACKUP_NEWSHEADLINE']._serialized_start=4639
+  _globals['_BACKUP_NEWSHEADLINE']._serialized_end=4679
+  _globals['_BACKUP_ACHIEVEMENTINFO']._serialized_start=4681
+  _globals['_BACKUP_ACHIEVEMENTINFO']._serialized_end=4728
+  _globals['_BACKUP_ACTIVEBOOST']._serialized_start=4730
+  _globals['_BACKUP_ACTIVEBOOST']._serialized_end=4810
+  _globals['_BACKUP_OWNEDBOOST']._serialized_start=4812
+  _globals['_BACKUP_OWNEDBOOST']._serialized_end=4857
+  _globals['_BACKUP_MISSIONINFO']._serialized_start=4859
+  _globals['_BACKUP_MISSIONINFO']._serialized_end=4928
+  _globals['_EGGINCFIRSTCONTACTREQUEST']._serialized_start=4930
+  _globals['_EGGINCFIRSTCONTACTREQUEST']._serialized_end=5030
+  _globals['_EGGINCFIRSTCONTACTRESPONSE']._serialized_start=5032
+  _globals['_EGGINCFIRSTCONTACTRESPONSE']._serialized_end=5088
+  _globals['_EGGINCADCONFIG']._serialized_start=5090
+  _globals['_EGGINCADCONFIG']._serialized_end=5147
+  _globals['_DAILYGIFTINFO']._serialized_start=5149
+  _globals['_DAILYGIFTINFO']._serialized_end=5214
+  _globals['_SALESINFOREQUEST']._serialized_start=5217
+  _globals['_SALESINFOREQUEST']._serialized_end=5417
+  _globals['_IAPSALEENTRY']._serialized_start=5419
+  _globals['_IAPSALEENTRY']._serialized_end=5522
+  _globals['_SALESINFO']._serialized_start=5524
+  _globals['_SALESINFO']._serialized_end=5568
+  _globals['_EGGINCEVENT']._serialized_start=5570
+  _globals['_EGGINCEVENT']._serialized_end=5682
+  _globals['_EGGINCCURRENTEVENTS']._serialized_start=5684
+  _globals['_EGGINCCURRENTEVENTS']._serialized_end=5738
+  _globals['_DEVICEINFO']._serialized_start=5741
+  _globals['_DEVICEINFO']._serialized_end=6038
+  _globals['_APPINFO']._serialized_start=6041
+  _globals['_APPINFO']._serialized_end=6766
+  _globals['_ACTIONKEYVALUEPAIR']._serialized_start=6768
+  _globals['_ACTIONKEYVALUEPAIR']._serialized_end=6816
+  _globals['_GENERICACTION']._serialized_start=6819
+  _globals['_GENERICACTION']._serialized_end=7013
+  _globals['_VERIFYPURCHASEREQUEST']._serialized_start=7015
+  _globals['_VERIFYPURCHASEREQUEST']._serialized_end=7142
+  _globals['_VERIFYPURCHASERESPONSE']._serialized_start=7144
+  _globals['_VERIFYPURCHASERESPONSE']._serialized_end=7203
+  _globals['_CONTRACT']._serialized_start=7206
+  _globals['_CONTRACT']._serialized_end=7795
+  _globals['_CONTRACT_GOAL']._serialized_start=7582
+  _globals['_CONTRACT_GOAL']._serialized_end=7750
+  _globals['_CONTRACT_GOALSET']._serialized_start=7752
+  _globals['_CONTRACT_GOALSET']._serialized_end=7795
+  _globals['_CONTRACTSREQUEST']._serialized_start=7797
+  _globals['_CONTRACTSREQUEST']._serialized_end=7858
+  _globals['_CONTRACTSRESPONSE']._serialized_start=7860
+  _globals['_CONTRACTSRESPONSE']._serialized_end=7981
+  _globals['_CONTRACTCOOPSTATUSREQUEST']._serialized_start=7983
+  _globals['_CONTRACTCOOPSTATUSREQUEST']._serialized_end=8081
+  _globals['_CONTRACTCOOPSTATUSRESPONSE']._serialized_start=8084
+  _globals['_CONTRACTCOOPSTATUSRESPONSE']._serialized_end=8826
+  _globals['_CONTRACTCOOPSTATUSRESPONSE_CONTRIBUTIONINFO']._serialized_start=8476
+  _globals['_CONTRACTCOOPSTATUSRESPONSE_CONTRIBUTIONINFO']._serialized_end=8762
+  _globals['_CONTRACTCOOPSTATUSRESPONSE_COOPGIFT']._serialized_start=8764
+  _globals['_CONTRACTCOOPSTATUSRESPONSE_COOPGIFT']._serialized_end=8826
+  _globals['_LOCALCONTRACT']._serialized_start=8829
+  _globals['_LOCALCONTRACT']._serialized_end=9244
+  _globals['_MYCONTRACTS']._serialized_start=9247
+  _globals['_MYCONTRACTS']._serialized_end=9424
+  _globals['_QUERYCOOPREQUEST']._serialized_start=9426
+  _globals['_QUERYCOOPREQUEST']._serialized_end=9538
+  _globals['_QUERYCOOPRESPONSE']._serialized_start=9540
+  _globals['_QUERYCOOPRESPONSE']._serialized_end=9631
+  _globals['_CREATECOOPREQUEST']._serialized_start=9634
+  _globals['_CREATECOOPREQUEST']._serialized_end=9862
+  _globals['_CREATECOOPRESPONSE']._serialized_start=9864
+  _globals['_CREATECOOPRESPONSE']._serialized_end=9918
+  _globals['_JOINCOOPREQUEST']._serialized_start=9921
+  _globals['_JOINCOOPREQUEST']._serialized_end=10120
+  _globals['_JOINCOOPRESPONSE']._serialized_start=10122
+  _globals['_JOINCOOPRESPONSE']._serialized_end=10242
+  _globals['_AUTOJOINCOOPREQUEST']._serialized_start=10245
+  _globals['_AUTOJOINCOOPREQUEST']._serialized_end=10450
+  _globals['_UPDATECOOPPERMISSIONSREQUEST']._serialized_start=10453
+  _globals['_UPDATECOOPPERMISSIONSREQUEST']._serialized_end=10605
+  _globals['_UPDATECOOPPERMISSIONSRESPONSE']._serialized_start=10607
+  _globals['_UPDATECOOPPERMISSIONSRESPONSE']._serialized_end=10672
+  _globals['_LEAVECOOPREQUEST']._serialized_start=10674
+  _globals['_LEAVECOOPREQUEST']._serialized_end=10797
+  _globals['_GIFTPLAYERCOOPREQUEST']._serialized_start=10800
+  _globals['_GIFTPLAYERCOOPREQUEST']._serialized_end=11002
+  _globals['_KICKPLAYERCOOPREQUEST']._serialized_start=11005
+  _globals['_KICKPLAYERCOOPREQUEST']._serialized_end=11161
+  _globals['_CONTRACTCOOPSTATUSUPDATEREQUEST']._serialized_start=11164
+  _globals['_CONTRACTCOOPSTATUSUPDATEREQUEST']._serialized_end=11392
+  _globals['_CONTRACTCOOPSTATUSUPDATERESPONSE']._serialized_start=11394
+  _globals['_CONTRACTCOOPSTATUSUPDATERESPONSE']._serialized_end=11447
+  _globals['_USERDATAINFOREQUEST']._serialized_start=11449
+  _globals['_USERDATAINFOREQUEST']._serialized_end=11531
+  _globals['_USERDATAINFORESPONSE']._serialized_start=11533
+  _globals['_USERDATAINFORESPONSE']._serialized_end=11633
+  _globals['_CLEARALLUSERDATAREQUEST']._serialized_start=11635
+  _globals['_CLEARALLUSERDATAREQUEST']._serialized_end=11721
+  _globals['_SERVERGIFT']._serialized_start=11723
+  _globals['_SERVERGIFT']._serialized_end=11837
+  _globals['_PERIODICALSRESPONSE']._serialized_start=11840
+  _globals['_PERIODICALSRESPONSE']._serialized_end=12005
+  _globals['_GETPERIODICALSREQUEST']._serialized_start=12008
+  _globals['_GETPERIODICALSREQUEST']._serialized_end=12247
+  _globals['_ADATTRIBUTIONRAWDATA']._serialized_start=12249
+  _globals['_ADATTRIBUTIONRAWDATA']._serialized_end=12349
+  _globals['_ADATTRIBUTIONROW']._serialized_start=12352
+  _globals['_ADATTRIBUTIONROW']._serialized_end=12536
+  _globals['_ADATTRIBUTIONINFO']._serialized_start=12539
+  _globals['_ADATTRIBUTIONINFO']._serialized_end=12936
+# @@protoc_insertion_point(module_scope)
diff --git a/events.py b/events.py
new file mode 100644 (file)
index 0000000..50634dc
--- /dev/null
+++ b/events.py
@@ -0,0 +1,92 @@
+# events.py - Event Scheduler for the server
+import ei_pb2 as EIProto
+import random
+import datetime
+import time
+
+###
+# BEHAVIOR ANALYSIS:
+# Every Sunday - Soul Eggs 2x/3x event
+# They typically last 24 hours
+# Multiple *can* occur at once, albeit rarely
+# Some events are more common than others.
+# Thank you https://wasmegg-carpet.netlify.app/events/ for providing a database of past events.
+###
+# Changes compared to original game:
+# No piggy bank events - you can't crack one open anyway.
+###
+# We can however, just lazily apply a "pre-set" map of events.
+# If it's multiple events, it's an array, else it's an EIProto.EggIncEvent
+###
+
+event_calendar = []
+def create_event_proto(type, internal_name, subtitle, multiplier):
+       evt = EIProto.EggIncEvent()
+       evt.type = type
+       evt.multiplier = multiplier
+       evt.subtitle = subtitle
+       evt.identifier = internal_name
+       return evt
+
+event_calendar.append([
+       create_event_proto("vehicle-sale", "VEHICLE SALE", "75% off all vehicles", 0.25),
+       create_event_proto("drone-boost", "GENEROUS DRONES", "5x drone rewards", 5.0)
+])
+event_calendar.append(create_event_proto("epic-research-sale", "EPIC RESEARCH SALE", "30% off Epic Research Upgrades", 0.7))
+event_calendar.append(create_event_proto("research-sale", "RESEARCH SALE", "70% off Research Upgrades", 0.3))
+event_calendar.append(create_event_proto("hab-sale", "HAB SALE", "70% OFF HEN HOUSES!", 0.3))
+event_calendar.append(create_event_proto("boost-sale", "BOOST SALE", "30% OFF BOOSTS!", 0.7))
+event_calendar.append(create_event_proto("earnings-boost", "CASH BOOST", "3x EARNINGS!", 3.0))
+event_calendar.append(create_event_proto("gift-boost", "GENEROUS GIFTS", "4x GIFTS!", 4.0))
+event_calendar.append(create_event_proto("boost-duration", "BOOST TIME+", "DOUBLE BOOST TIME", 2.0))
+event_calendar.append(create_event_proto("hab-sale", "HAB SALE", "70% OFF HEN HOUSES!", 0.3))
+event_calendar.append(create_event_proto("vehicle-sale", "VEHICLE SALE", "75% off all vehicles", 0.25))
+event_calendar.append(create_event_proto("drone-boost", "GENEROUS DRONES", "5x drone rewards", 5.0))
+event_calendar.append(create_event_proto("research-sale", "RESEARCH SALE", "70% off Research Upgrades", 0.3))
+event_calendar.append(create_event_proto("boost-sale", "BOOST SALE", "30% OFF BOOSTS!", 0.7))
+event_calendar.append(create_event_proto("earnings-boost", "CASH BOOST", "3x EARNINGS!", 3.0))
+event_calendar.append(create_event_proto("hab-sale", "HAB SALE", "70% OFF HEN HOUSES!", 0.3))
+event_calendar.append(create_event_proto("boost-duration", "BOOST TIME+", "DOUBLE BOOST TIME", 2.0))
+event_calendar.append(create_event_proto("gift-boost", "GENEROUS GIFTS", "4x GIFTS!", 4.0))
+event_calendar.append(create_event_proto("epic-research-sale", "EPIC RESEARCH SALE", "30% off Epic Research Upgrades", 0.7))
+event_calendar.append(create_event_proto("vehicle-sale", "VEHICLE SALE", "75% off all vehicles", 0.25))
+event_calendar.append(create_event_proto("drone-boost", "GENEROUS DRONES", "5x drone rewards", 5.0))
+event_calendar.append(create_event_proto("boost-duration", "BOOST TIME+", "DOUBLE BOOST TIME", 2.0))
+event_calendar.append(create_event_proto("research-sale", "RESEARCH SALE", "70% off Research Upgrades", 0.3))
+event_calendar.append(create_event_proto("earnings-boost", "CASH BOOST", "3x EARNINGS!", 3.0))
+event_calendar.append(create_event_proto("hab-sale", "HAB SALE", "70% OFF HEN HOUSES!", 0.3))
+event_calendar.append(create_event_proto("gift-boost", "GENEROUS GIFTS", "4x GIFTS!", 4.0))
+event_calendar.append(create_event_proto("vehicle-sale", "VEHICLE SALE", "75% off all vehicles", 0.25))
+event_calendar.append(create_event_proto("drone-boost", "GENEROUS DRONES", "5x drone rewards", 5.0))
+event_calendar.append(create_event_proto("earnings-boost", "CASH BOOST", "3x EARNINGS!", 3.0))
+event_calendar.append(create_event_proto("epic-research-sale", "EPIC RESEARCH SALE", "30% off Epic Research Upgrades", 0.7))
+event_calendar.append(create_event_proto("boost-duration", "BOOST TIME+", "DOUBLE BOOST TIME", 2.0))
+event_calendar.append(create_event_proto("hab-sale", "HAB SALE", "70% OFF HEN HOUSES!", 0.3))
+
+triple_prestige_event = create_event_proto("prestige-boost", "PRESTIGE BOOST", "TRIPLE PRESTIGE!", 3.0)
+double_prestige_event = create_event_proto("prestige-boost", "PRESTIGE BOOST", "DOUBLE PRESTIGE!", 2.0)
+
+def get_active_events():
+       date = datetime.datetime.today()
+       delta = (datetime.datetime.combine(date + datetime.timedelta(days=1), datetime.time.min) - date).seconds
+       cur_day = date.day
+       res = []
+       # it's a sunday, we have a fixed prestige event
+       if date.isoweekday() == 7:
+               # create a seed based off year and week to determine if we should use triple or double prestige this week
+               iso_calendar = date.isocalendar()
+               seed = int(str(iso_calendar.year) + str(iso_calendar.week))
+               srng = random.Random(seed)
+               # 50/50 chance of it being either
+               if srng.random() < 0.5:
+                       res.append(double_prestige_event)
+               else:
+                       res.append(triple_prestige_event)
+       if type(event_calendar[cur_day]) is list:
+               res = event_calendar[cur_day]
+       else:
+               res.append(event_calendar[cur_day])
+       # Apply duration deltas
+       for event in res:
+               event.seconds_remaining = delta
+       return res
diff --git a/requirements.txt b/requirements.txt
new file mode 100644 (file)
index 0000000..ae97318
--- /dev/null
@@ -0,0 +1,9 @@
+blinker==1.8.2
+click==8.1.7
+Flask==3.0.0
+google==3.0.0
+itsdangerous==2.2.0
+Jinja2==3.1.4
+MarkupSafe==2.1.5
+protobuf==4.24.4
+Werkzeug==3.0.3