Package net.cloudcodex.server.service

Source Code of net.cloudcodex.server.service.CampaignService

package net.cloudcodex.server.service;


import static net.cloudcodex.shared.Errors.IMPOSSIBLE_EXISTS;
import static net.cloudcodex.shared.Errors.NOT_FOUND_CAMPAIGN;
import static net.cloudcodex.shared.Errors.NOT_FOUND_CHARACTER;
import static net.cloudcodex.shared.Errors.NOT_FOUND_USER;
import static net.cloudcodex.shared.Errors.REQUIRED;
import static net.cloudcodex.shared.Errors.USER_USURPATION_GM;
import static net.cloudcodex.shared.Errors.USER_USURPATION_NPC;
import static net.cloudcodex.shared.Errors.USER_USURPATION_PC;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;

import net.cloudcodex.server.Context;
import net.cloudcodex.server.data.Data;
import net.cloudcodex.server.data.Data.Campaign;
import net.cloudcodex.server.data.Data.CharacterNote;
import net.cloudcodex.server.data.Data.User;
import net.cloudcodex.server.data.campaign.character.CharacterDescriptionSDO;
import net.cloudcodex.server.data.campaign.character.CharacterSDO;
import net.cloudcodex.shared.Errors;

import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.Query;
import com.google.appengine.api.datastore.Query.FilterOperator;
import com.google.appengine.api.datastore.Transaction;

/**
* Service for Campaign
* @author Thomas
*/
public class CampaignService extends AbstractCampaignService {

  /**
   * @param store Google AppEngine datastore.
   */
  public CampaignService(DatastoreService store) {
    super(store);
  }
 
  /**
   * Creates a campaign.
   *
   * @param name name of the campaign to create.
   * @param master game master
   * @return the newly created campaign object.
   */
  public Data.Campaign createCampaign(Context context, String name,
      String game, String description, String icon,
      Data.User master) {
   
    if(master == null || name == null) {
      logger.severe("missing param");
      context.addError(REQUIRED);
      return null;
    }
   
    // check the campaign doesn't already exist
    Campaign campaign = dao.getCampaignByName(context, name);
    if(campaign != null) {
      logger.severe("campaign '" + name + "' alreayd exists");
      context.addError(IMPOSSIBLE_EXISTS, name);
      return null;
    }
   
    campaign = new Campaign();
    campaign.setName(name);
    campaign.setGame(game);
    campaign.setMaster(master);
    campaign.setDate(new Date());
    campaign.setDescription(description);
    campaign.setIcon(icon);
    campaign.setDate(new Date());
    dao.save(context, campaign);

    return campaign;
  }
 
  /**
   * Updates a campaign.
   *
   * @param campaignId id of the campaign to update.
   * @param name name of the campaign
   * @param master game master
   * @return the newly created campaign object.
   */
  public Data.Campaign updateCampaign(Context context, long campaignId,
      String name, String game, String description, String icon,
      Data.User master) {
   
    if(master == null || name == null) {
      logger.severe("missing param");
      context.addError(REQUIRED);
      return null;
    }
   
    // Check campaign exists.
    final Data.Campaign campaign = dao.readCampaign(context, campaignId);
   
    if(campaign == null) {
      logger.severe("Campaign not found " + campaignId);
      context.addError(NOT_FOUND_CAMPAIGN, campaignId);
      return null;
    }
   
    // Check current user is campaign GM
    final Data.User user = context.getUser();
    if(!isGameMaster(context, campaign)) {
      logger.severe("Unauthorized access by " + user.getKey() + " on " + campaignId);
      context.addError(USER_USURPATION_GM);
      return null;
    }
   
    // check the campaign doesn't already exist
    Campaign otherCampaign = dao.getCampaignByName(context, name);
    if(otherCampaign != null) {
      logger.severe("campaign '" + name + "' alreayd exists");
      context.addError(IMPOSSIBLE_EXISTS, name);
      return null;
    }
   
    campaign.setName(name);
    campaign.setGame(game);
    campaign.setDescription(description);
    campaign.setIcon(icon);
   
    dao.save(context, campaign);

    return campaign;
  }
 

  public Data.Character createCharacter(Context context, Campaign campaign, String name,
      User owner, String icon, String description) {
   
    return createCharacter(context, campaign, name, owner, icon, description, false);
  }

  /**
   * Creates a character if its doesn't already exists.
   *
   * @param campaign campaign of the character.
   * @param name name of the character.
   * @param owner owner (user) of the character.
   * @return the newly created character.
   */
  public Data.Character createCharacter(Context context, Campaign campaign, String name,
      User owner, String icon, String description, boolean profile) {
   
    if(campaign == null || name == null) {
      logger.severe("missing param");
      context.addError(REQUIRED);
      return null;
    }
   
    name = name.trim();
   
    // check the character doesn't already exist
    Data.Character character =
        dao.getCharacterByName(context, campaign.getKey(), name);
    if(character != null) {
      logger.severe("character '" + name
          + "' alreayd exists in " + campaign.getKey());
      context.addError(IMPOSSIBLE_EXISTS, name, campaign.getKey());
      return null;
    }
   
    character = new Data.Character(campaign);
    character.setName(name);
    character.setOwner(owner);
    character.setOwnerNickname(owner == null ? null : owner.getNickname());
    character.setDate(new Date());
    character.setIcon(icon);
    character.setDescription(description);
    character.setProfile(profile);
    dao.save(context, character);
    return character;
  }

  /**
   * Add a user to a campaign.
   */
  public CharacterSDO inviteToCampaign(Context context, long campaignId, String email, String name) {
   
    if(email == null || name == null) {
      logger.severe("missing param");
      context.addError(REQUIRED);
      return null;
    }
   
    // Check campaign exists.
    final Data.Campaign campaign = dao.readCampaign(context, campaignId);
   
    if(campaign == null) {
      logger.severe("Campaign not found " + campaignId);
      context.addError(NOT_FOUND_CAMPAIGN, campaignId);
      return null;
    }

    // Check current user is campaign GM
    final Data.User user = context.getUser();
    if(!isGameMaster(context, campaign)) {
      logger.severe("Unauthorized access by " + user.getKey() + " on " + campaignId);
      context.addError(USER_USURPATION_GM);
      return null;
    }

    // Check the user to add
    final Data.User player = dao.getUserByEmail(context, email);
    if(player == null) {
      logger.severe("User not found " + email);
      context.addError(NOT_FOUND_USER, email);
      return null;
    }
   
    // Check the GM is not inviting itself
    if(StringUtils.equals(player.getEmail(), context.getUser().getEmail())) {
      logger.severe("GM is inviting itself");
      context.addError(Errors.IMPOSSIBLE_YOURSELF);
      return null;
    }
   
    // Create the character.
    final Data.Character character =
      createCharacter(context, campaign, name, player, null, null);

    if(character == null) {
      return null;
    }
   
    final CharacterSDO sdo = new CharacterSDO();
    sdo.setCampaign(campaign);
    sdo.setCharacter(character);
   
    return sdo;
  }
 
  /**
   * Creates a NPC.
   * @param context execution context.
   * @param campaignId campaign's id.
   * @param name NPC's name, cannot be null.
   * @param icon NPC's icon.
   * @param description NPC's description.
   * @param <code>true</code> for profiles.
   * @return the newly created NPC.
   */
  public CharacterSDO createNPC(Context context, long campaignId,
      String name, String icon, String description, boolean profile) {
   
    if(name == null) {
      logger.severe("no name specified");
      context.addError(REQUIRED, "name");
      return null;
    }
   
    // Check campaign exists.
    final Data.Campaign campaign = dao.readCampaign(context, campaignId);
   
    if(campaign == null) {
      logger.severe("Campaign not found " + campaignId);
      context.addError(NOT_FOUND_CAMPAIGN, campaignId);
      return null;
    }

    // Check current user is campaign GM
    final Data.User user = context.getUser();
    if(!campaign.getMaster().equals(user.getKey())) {
      logger.severe("Unauthorized access by "
          + user.getKey() + " on " + campaignId);
      context.addError(USER_USURPATION_GM);
      return null;
    }

    // Create the character.
    final Data.Character character =
      createCharacter(context, campaign, name, null, icon, description, profile);

    if(character == null) {
      return null;
    }
   
    final CharacterSDO sdo = new CharacterSDO();
    sdo.setCampaign(campaign);
    sdo.setCharacter(character);
   
    return sdo;
  }
 
  /**
   * Returns the campaign's characters, only GM cans call this method.
   * @param context execution context.
   * @param campaignId campaign id.
   * @return the campaign's characters.
   */
  public List<CharacterSDO> getCampaignCharacters(Context context, long campaignId) {
   
    final Campaign campaign = dao.readCampaign(context, campaignId);
    if(campaign == null) {
      logger.severe("Campaign not found " + campaignId);
      context.addError(NOT_FOUND_CAMPAIGN, campaignId);
      return null;
    }

    final Key campaignKey = campaign.getKey();
   
    // Check current user is campaign GM
    final Data.User user = context.getUser();
    if(!campaign.getMaster().equals(user.getKey())) {
      logger.severe("Unauthorized access by "
          + user.getKey() + " on " + campaignKey);
      context.addError(USER_USURPATION_GM);
      return null;
    }

    // Get all characters
    final List<Data.Character> characters = dao.asListOfCharacters(
        context, dao.queryCharacter(campaignKey), null);
   
    if(characters == null || characters.isEmpty()) {
      return null;
    }
   
    // Aggregate all data.
    final List<CharacterSDO> sdos = new ArrayList<CharacterSDO>();
    for(Data.Character character : characters) {
      final CharacterSDO sdo = new CharacterSDO();
      sdo.setCampaign(campaign);
      sdo.setCharacter(character);
      sdos.add(sdo);
    }
   
    return sdos.isEmpty() ? null : sdos;
  }
 
  /**
   * Consult the current user character header.
   *
   * @param context execution context.
   * @param campaignId campaign's id.
   * @param characterId character's id.
   * @return the character's header.
   */
  public CharacterSDO getCharacter(Context context, long campaignId, long characterId) {
   
    final Campaign campaign = dao.readCampaign(context, campaignId);
    if(campaign == null) {
      logger.severe("Campaign not found " + campaignId);
      context.addError(NOT_FOUND_CAMPAIGN, campaignId);
      return null;
    }

    final Key campaignKey = campaign.getKey();
    final Key characterKey = Data.Character.createKey(campaignKey, characterId);
   
    // Check the character exists
    final Data.Character character = dao.readCharacter(context, characterKey);
    if(character == null) {
      logger.severe("Invalid character " + characterKey);
      context.addError(NOT_FOUND_CHARACTER, characterId);
      return null;
    }

    // check its not a NPC
    if(character.getOwner() == null) {
      logger.severe("User " + context.getUser().getKey()
          + " tried to get character description as NPC");
      context.addError(USER_USURPATION_NPC);
      return null;
    }
   
    // check user is its owner.
    if(!isOwner(context, character) && !isGameMaster(context, campaign)) {
      logger.severe("User " + context.getUser().getKey()
          + " tried to get character description as "
          + characterKey);
      context.addError(USER_USURPATION_PC);
      return null;
    }
   
    final CharacterSDO sdo = new CharacterSDO();
    sdo.setCampaign(campaign);
    sdo.setCharacter(character);
   
    return sdo;
  }
 
 
 
  /**
   * @param context execution context.
   * @param campaignId camapign's id.
   * @param characterId character's id.
   * @param timestamp last character description timestamp, may be null.
   * @return the character description, or just the changes if timestamp is not null.
   */
  public CharacterDescriptionSDO getCharacterDescription(
    Context context, long campaignId, long characterId,
    Long byCharacterId, Date timestamp) {

    final Key campaignKey = Campaign.createKey(campaignId);
    final Data.Campaign campaign = dao.readCampaign(context, campaignKey);
    if(campaign == null) {
      logger.severe("Invalid campaign " + campaignKey);
      context.addError(NOT_FOUND_CAMPAIGN, campaignId);
      return null;
    }

    final Key characterKey = Data.Character.createKey(campaignKey, characterId);

    // Check the user is GM
    final boolean master = isGameMaster(context, campaign);
   
    if(!master && byCharacterId == null) {
      logger.severe("User " + context.getUser().getKey()
          + " tried to get character description as GM");
      context.addError(USER_USURPATION_GM);
      return null;
    }
   
    // Done before reading to avoid a concurrent insert never read after.
    final Date readTimestamp = new Date();
   
    final Query query;
    if(timestamp != null) {
      // Get notes with timestamp field > timestamp param
      query = dao.addCharacterNoteFilterOnTimestamp(
              dao.queryCharacterNote(characterKey),
              FilterOperator.GREATER_THAN, timestamp);
    } else {
      query = dao.queryCharacterNote(characterKey);
    }
   
    final CharacterDescriptionSDO description = new CharacterDescriptionSDO();
    description.setTimestamp(readTimestamp);
    description.setCampaignId(campaignId);
    description.setCharacterId(characterId);

    // Get the character entity to test if its a profile or not.
    final Data.Character character = dao.readCharacter(context, characterKey);
    if(character == null) {
      logger.severe("Invalid character " + characterKey);
      context.addError(NOT_FOUND_CHARACTER, characterId);
      return null;
    }

    if(!Boolean.TRUE.equals(character.getProfile())) {
      if(master) {
        // GM sees all notes
        description.setNotes(dao.asListOfCharacterNotes(context, query, null));
       
        // GMM sees the character's sheet.
        description.setSheet(character.getSheet());

      } else {
        // check the character viewing exists
        final Key byCharacterKey = Data.Character.createKey(campaignKey, byCharacterId);
        final Data.Character byCharacter = dao.readCharacter(context, byCharacterKey);
        if(byCharacter == null) {
          logger.severe("Invalid character " + byCharacterKey);
          context.addError(NOT_FOUND_CHARACTER, byCharacterId);
          return null;
        }

        // check its not a NPC
        if(byCharacter.getOwner() == null) {
          logger.severe("User " + context.getUser().getKey()
              + " tried to get character description as NPC");
          context.addError(USER_USURPATION_NPC);
          return null;
        }

        // check the user is its owner
        if(!isOwner(context, byCharacter)) {
          logger.severe("User " + context.getUser().getKey()
              + " tried to get character description as "
              + byCharacterKey);
          context.addError(USER_USURPATION_PC);
          return null;
        }
       
        // Itself and others players can only see their own.
        description.setNotes(
            dao.asListOfCharacterNotes(context,
                dao.addCharacterNoteFilterOnAuthor(
                query, FilterOperator.EQUAL, byCharacterKey), null));
       
        // the character consults itself
        if(byCharacterId != null && characterId == byCharacterId) {
          description.setSheet(byCharacter.getSheet());
        }
      }
    }

    if(master) {
      // map the character aliases.
      final Map<String, String> aliases = character.getAlias();
      if(aliases != null) {
        final Map<Long, String> descAliases = new HashMap<Long, String>();
        for(Map.Entry<String, String> entry : aliases.entrySet()) {
          try {
            final Long charId = Long.valueOf(entry.getKey());
            descAliases.put(charId, entry.getValue());
          } catch(NumberFormatException e) {
            logger.severe(entry.getKey()
              + " is not a valid charId for alias in " + character);
          }
        }
        if(descAliases != null) {
          description.setAliases(descAliases);
        }
      }
    }
   
    return description;
  }

  /**
   * Update a character description and notes about.
   *
   * @param context execution context.
   * @param campaignId campaign's id.
   * @param characterId description's character's id.
   * @param byCharacterId id of the character updating the description,
   *     <code>null</code> for game master.
   * @param description description to update, null to not update.
   * @param notes map author/note to update.
   * @param notes map PC/alias to update, only for GM.
   *
   * @return the result of {@link #getCharacterDescription(
   *     Context, long, long, Date)} after the update.
   */
  public CharacterDescriptionSDO updateCharacterDescription(
    Context context, long campaignId, long characterId, Long byCharacterId,
    String description, String sheet, Boolean dead, Boolean locked,
    Map<Long, String> notes, Map<Long, String> aliases) {

    // Check the campaign and the master
    final Key campaignKey = Data.Campaign.createKey(campaignId);
    final Campaign campaign = dao.readCampaign(context, campaignKey);
    if(campaign == null) {
      logger.severe("Campaign not found " + campaignKey);
      context.addError(NOT_FOUND_CAMPAIGN, campaignId);
      return null;
    }

    // see if the user is game master
    final boolean master = isGameMaster(context, campaign);
   
    // Check the user to update exists
    final Key characterKey = Data.Character.createKey(campaignKey, characterId);
    final Data.Character character = dao.readCharacter(context, characterKey);
    if(character == null) {
      logger.severe("character not found " + characterKey);
      context.addError(NOT_FOUND_CHARACTER, characterId);
      return null;
    }
   
    if(Boolean.TRUE.equals(character.getProfile())
        && notes != null && !notes.isEmpty()) {
      logger.severe("Character " + characterKey + " is a profile, "
          + context.getUser().getKey() + " cannot make notes");
      context.addError(Errors.IMPOSSIBLE_PROFILE);
      return null;
    }
   
    final boolean npc = character.getOwner() == null;
   
    // Do the updates in a single transaction
    final Transaction tx = dao.beginTransaction();
    try {
      if(master) {
        // Update the character's description.
        if(description != null) {
          if("".equals(description.trim())) {
            character.setDescription(null);
          } else {
            character.setDescription(description);
          }
        }
       
        // Update the character's sheet.
        if(sheet != null) {
          if("".equals(sheet.trim())) {
            character.setSheet(null);
          } else {
            character.setSheet(sheet);
          }
        }

        // Update the character's aliases
        if(aliases != null) {
          for(Map.Entry<Long, String> entry : aliases.entrySet()) {
            if(entry.getKey() != null) {
              character.setAlias(entry.getKey().toString(), entry.getValue());
            }
          }
        }
       
        // master can update all notes. (but profile can't have notes)
        if(notes != null) {
          for(Map.Entry<Long, String> entry : notes.entrySet()) {

            // author's key is null for GM
            final Key authorKey = entry.getKey() == null ? null
                : Data.Character.createKey(campaignKey, entry.getKey());

            // read the current note from this author
            CharacterNote noteDB = dao.getCharacterNoteByAuthor(context, characterKey, authorKey);
            if(noteDB == null) {
              noteDB = new CharacterNote(character);
              noteDB.setAuthor(authorKey);
            }
           
            // Notes are never deleted, just set to null (usefull when checking deltas)
            noteDB.setContent(entry.getValue());
            dao.save(context, noteDB);
          }
        }
       
        if(dead != null) {
          character.setDead(dead);
        }

        // Only PCs can be locked
        if(locked != null && !npc) {
          character.setLocked(locked);
        }
       
        dao.save(context, character);

      } else {
       
        if(notes != null) {
          if(byCharacterId == null) {
            logger.severe("user " + context.getUser().getKey()
                + " has tried to update GM note on " + characterKey);
            context.addError(USER_USURPATION_GM);
            return null;
          }
         
          // players can only update their own.
          final Key authorKey = Data.Character.createKey(campaignKey, byCharacterId);

          // check the writer exists
          final Data.Character author = dao.readCharacter(context, authorKey);
          if(author == null) {
            logger.severe("character not found " + characterKey);
            context.addError(NOT_FOUND_CHARACTER, characterId);
            return null;
          }
         
          // check the user is the writer's owner
          if(!isOwner(context, author)) {
            logger.severe("user " + context.getUser().getKey() + " has tried to update "
                + author.getOwner() + "'s note on " + characterKey);
            context.addError(Errors.USER_USURPATION);
            return null;
          }

          // update or create the note if not an NPC
          CharacterNote noteDB = dao.getCharacterNoteByAuthor(context, characterKey, authorKey);
          if(noteDB == null) {
            noteDB = new CharacterNote(character);
            noteDB.setAuthor(authorKey);
          }
          noteDB.setContent(notes.get(byCharacterId));
          dao.save(context, noteDB);
        }
      }

      tx.commit();
     
    } finally {
      if(tx.isActive()) {
        tx.rollback();
      }
    }
   
    return getCharacterDescription(
        context, campaignId, characterId, byCharacterId, null);
  }
 

 
 
 
 

 
 
 
 
 
 
}
TOP

Related Classes of net.cloudcodex.server.service.CampaignService

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.