On Github mstaples / GameDev
I've been developing a Social Strategy City-Builder RPG
game of awesomeness.
Planning
Inventory Design Info
Locate, Categorize, Analyze.
Planning
Conceptualize Development
Planning
Initial Database Design
Resources
Goods
Buildings
Items
Actions
Districts
Classes
Attributes
Units
Planning
Learn the Framework
harvesting, processing, crafting, training, building, actions, leveling
Core Systems
Initial Database Content
Install Doctrine Fixtures Bundle
Create Entities
> composer require doctrine/doctrine-fixtures-bundle
// src/Group/NameBundle/Entity/Resources.php /** * @ORM\Entity * @ORM\Table(name="resources") */
> php app/console doctrine:schema:update --force
> php app/console doctrine:generate:entities Group/NameBundle/Entity/Resources
Core Systems
Initial Database Content
Design Documents
Fixture Spreadsheets
Export to CSV
// [ProjectDirectory]/src/[Group]/[Bundle]/Datafixtures/ORM/exports
Core Systems
Initial Database Content
// src/Group/NameBundle/DataFixtures/ORM/LoadResourcesData.php namespace Group\NameBundle\DataFixtures\ORM; use Doctrine\Common\DataFixtures\FixtureInterface; use Doctrine\Common\Persistence\ObjectManager; use Group\NameBundle\Entity\Resources; class LoadResourcesData implements FixtureInterface { public function load(ObjectManager $manager) { $filename = __DIR__.'/exports/resources.txt'; $fd = fopen($filename, "r"); $contents = fread($fd, 10000); fclose($fd); $splitcontents = explode(',', $contents); $counter=0; foreach ($splitcontents as $each) { if ($counter == 0) { $resource = new Resources(); $resource->setName($each); } ... } } }
> php app/console doctrine:fixtures:load
Core Systems
Initial Database Content
// src/Group/NameBundle/Command/startOneCommand.php namespace Group\NameBundle\Command; use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; use Symfony\Component\Console\Output\OutputInterface; use ONN\BrunBundle\Entity\Resources; class startOneCommand extends ContainerAwareCommand { protected function configure() { $this->setName('start:one') ->setDescription('setup the game server'); } protected function execute(InputInterface $input, OutputInterface $output) { $em = $this->getContainer()->get('doctrine')->getManager('default'); $output->writeln('step one process: generating Wilderness'); $this->generateWilderness(); $output->writeln('... complete'); } }
> php app/console start:one
Core Systems
Initial Interactions
Core Systems
Initial Time Based Events
Timed Event Tables:
Core Systems
Initial Time Based Events
// src/Group/NameBundle/Command/timeCommand.php ... class timeCommand extends ContainerAwareCommand { protected function configure() { $this ->setName('time') ->setDescription('Triggers timed event processing') ->setDefinition(array( new InputArgument( 'timer', InputArgument::REQUIRED, 'timer name' ), )); } protected function execute(InputInterface $input, OutputInterface $output) { $timer = $input->getArgument('timer'); } }
> php app/console time days
Core Systems
Initial Time Based Events
#!/bin/bash # scripts/brunday.bsh logfile=~/logs/bruncycle.log exec >> $logfile 2>&1 export PATH echo "Brun Daily Cycle:" >> ~/logs/bruncycle.log php ~/www/app/console time days
> crontab -e 0 0 1 * * ~/scripts/brunback.bsh 01 * * * * ~/scripts/brunday.bsh 01 01 * * * ~/scripts/brunweek.bsh */15 * * * * ~/scripts/brunhour.bsh */5 * * * * ~/scripts/checkcycle.bsh
Core Systems
Initial UI
public function someFormAction(Request $request, $active = 'no') { $form_name = "someForm"; $form_path = "some_form"; $form = $this->createFormBuilder(null) ... ->getForm(); return $this->render('GroupNameBundle:None:formFragment.html.twig', [ 'form' => $form->createView(), 'form_name'=>$form_name, 'form_path'=>$form_path )); }
Core Systems
Initial UI
{# src/Group/NameBundle/Resources/views/None/formFragment.html.twig #} <script> function {{ form_name }}Submit() { $.post( '{{ path( form_path, { 'active': 'yes' }) }}', $('#form_{{ form_name }}').serialize(), function(data){ $('#container_{{ form_name }}').empty().append(data); } ); return false; } </script> <div id="container_{{ form_name }}"> <form id='form_{{ form_name }}' method="post" {{ form_enctype(form) }} name="{{ form_name }}" > {{ form_widget(form) }} <input id="{{ form_name }}" onclick="{{ form_name }}Submit()" value='Submit' class='btn btn-small btn-custom' /> </form> </div>
Core Systems
First Exposure to "Community"
Core Systems
Expanded Interactions
dissolution of assumptions
Player Testing
Players Do Things Differently
Player Testing
Communicating with Players
Player Testing
Prioritizing Reports and Requests
Feature Completion
Organization Concerns
Feature Completion
Peripheral Features
Less Flexible, Harder Refactors
Feature Completion
Deliberate Testing
Feature Completion
Time Based Events: Redux
> composer require oldsound/rabbitmq:1.*
# app/config/config.yml old_sound_rabbit_mq: connections: default: host: 'localhost' port: 5672 user: %rabbit_user% password: %rabbit_password% ... producers: queue_task: ... consumers: queue_task: ...
Feature Completion
Time Based Events: Redux
# src/Group/NameBundle/Consumer/ProcessQueueConsumer.php ... class ProcessQueueConsumer implements ConsumerInterface { public function __construct(EntityManager $entityManager, $kernel) { ... } public function execute(AMQPMessage $msg) { $kernel = $this->kernel; $command = $msg->body; ... $path = str_replace('scripts','','php '. $kernel->getRootDir() . '/console ' . $command); $process = new Process($path); $process->start(); ... return $process->getOutput(); } }
$command = 'update-act-access '.$char_id; $this->get('old_sound_rabbit_mq.queue_task_producer')->publish($command);
Feature Completion
Time Based Events: Redux
#!/bin/bash # scripts/checkworkers.bsh NB_TASKS=15 SYMFONY_ENV="prod" TEXT[0]="app/console rabbitmq:consumer -l 256 queue_task" for text in "${TEXT[@]}" do NB_LAUNCHED=$(ps ax | grep "$text" | grep -v grep | wc -l) TASK="/usr/bin/env php ${text} --env=${SYMFONY_ENV}" for (( i=${NB_LAUNCHED}; i<${NB_TASKS}; i++ )) do echo "$(date +%c) - Launching a new consumer" nohup $TASK > consumer_$i.log & done done
Feature Completion
Reflecting Change for Active Players
Feature Completion
API
Feature Completion
3rd Party Code Upgrades
Feature Completion
Estimates are Hard
Feature Completion
3rd Party Code Upgrades
twitter: @dead_lugosi
freenode: deadlugosi
email: mstaples@aesopgames.com