ManaPlus
playerrelations.cpp
Go to the documentation of this file.
1 /*
2  * The ManaPlus Client
3  * Copyright (C) 2008-2009 The Mana World Development Team
4  * Copyright (C) 2009-2010 The Mana Developers
5  * Copyright (C) 2011-2019 The ManaPlus Developers
6  * Copyright (C) 2019-2021 Andrei Karas
7  *
8  * This file is part of The ManaPlus Client.
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program. If not, see <http://www.gnu.org/licenses/>.
22  */
23 
24 #include "being/playerrelations.h"
25 
26 #include "actormanager.h"
27 #include "configuration.h"
28 #include "logger.h"
29 
30 #include "being/localplayer.h"
32 #include "being/playerrelation.h"
33 
34 #include "utils/dtor.h"
35 #include "utils/foreach.h"
36 #include "utils/gettext.h"
37 
39 
40 #include "debug.h"
41 
42 static const unsigned int FIRST_IGNORE_EMOTE = 14;
43 
44 typedef std::map<std::string, PlayerRelation *> PlayerRelations;
45 typedef PlayerRelations::const_iterator PlayerRelationsCIter;
46 typedef std::list<PlayerRelationsListener *> PlayerRelationListeners;
47 typedef PlayerRelationListeners::const_iterator PlayerRelationListenersCIter;
48 
49 static const char *const PLAYER_IGNORE_STRATEGY_NOP = "nop";
50 static const char *const PLAYER_IGNORE_STRATEGY_EMOTE0 = "emote0";
51 static const char *const DEFAULT_IGNORE_STRATEGY =
53 
54 static const char *const NAME = "name";
55 static const char *const RELATION = "relation";
56 
57 static const unsigned int IGNORE_EMOTE_TIME = 100;
58 
59 namespace
60 {
62  {
63  public:
65  { }
66 
68 
69  bool operator() (const std::string &str1,
70  const std::string &str2) const
71  {
72  std::string s1 = str1;
73  std::string s2 = str2;
74  toLower(s1);
75  toLower(s2);
76  if (s1 == s2)
77  return str1 < str2;
78  return s1 < s2;
79  }
81 
82  // (De)serialisation class
84  public ConfigurationListManager<std::pair<std::string,
85  PlayerRelation *>, std::map<std::string, PlayerRelation *> *>
86  {
87  public:
89  { }
90 
92 
93  ConfigurationObject *writeConfigItem(
94  const std::pair<std::string, PlayerRelation *> &value,
95  ConfigurationObject *const cobj) const override final
96  {
97  if (cobj == nullptr ||
98  value.second == nullptr)
99  {
100  return nullptr;
101  }
102  cobj->setValue(NAME, value.first);
103  cobj->setValue(RELATION, toString(
104  CAST_S32(value.second->mRelation)));
105 
106  return cobj;
107  }
108 
109  std::map<std::string, PlayerRelation *> *
111  std::map<std::string, PlayerRelation *>
112  *const container) const override final
113  {
114  if (cobj == nullptr ||
115  container == nullptr)
116  {
117  return container;
118  }
119  const std::string name = cobj->getValue(NAME, "");
120  if (name.empty())
121  return container;
122 
123  if ((*container)[name] == nullptr)
124  {
125  const int v = cobj->getValueInt(RELATION,
127 
128  (*container)[name] = new PlayerRelation(
129  static_cast<RelationT>(v));
130  }
131  // otherwise ignore the duplicate entry
132 
133  return container;
134  }
135  };
136 } // namespace
137 
138 static PlayerConfSerialiser player_conf_serialiser; // stateless singleton
139 
140 const unsigned int PlayerRelation::RELATION_PERMISSIONS[RELATIONS_NR] =
141 {
142  /* NEUTRAL */ 0, // we always fall back to the defaults anyway
143  /* FRIEND */ EMOTE | SPEECH_FLOAT | SPEECH_LOG | WHISPER | TRADE,
144  /* DISREGARDED*/ EMOTE | SPEECH_FLOAT,
145  /* IGNORED */ 0,
146  /* ERASED */ INVISIBLE,
147  /* BLACKLISTED */ SPEECH_LOG | WHISPER,
148  /* ENEMY2 */ EMOTE | SPEECH_FLOAT | SPEECH_LOG | WHISPER | TRADE
149 };
150 
152  mPersistIgnores(false),
153  mDefaultPermissions(PlayerRelation::DEFAULT),
154  mIgnoreStrategy(nullptr),
155  mRelations(),
156  mListeners(),
157  mIgnoreStrategies()
158 {
159 }
160 
162 {
164 
166  delete it->second;
167  mRelations.clear();
168 }
169 
171 {
172  StringVect *const names = getPlayers();
173  FOR_EACHP (StringVectCIter, it, names)
174  removePlayer(*it);
175  delete names;
176 }
177 
178 static const char *const PERSIST_IGNORE_LIST = "persistent-player-list";
179 static const char *const PLAYER_IGNORE_STRATEGY = "player-ignore-strategy";
180 static const char *const DEFAULT_PERMISSIONS = "default-player-permissions";
181 
183  const std::string &name)
184 {
185  const STD_VECTOR<PlayerIgnoreStrategy *> *const strategies
187 
188  if (strategies == nullptr)
189  return -1;
190 
191  const size_t sz = strategies->size();
192  for (size_t i = 0; i < sz; i++)
193  {
194  if ((*strategies)[i]->mShortName == name)
195  return CAST_S32(i);
196  }
197 
198  return -1;
199 }
200 
202 {
203  Configuration *const cfg = &serverConfig;
204  clear();
205 
206  mPersistIgnores = (cfg->getValue(PERSIST_IGNORE_LIST, 1) != 0);
209 
210  const std::string ignore_strategy_name = cfg->getValue(
212  const int ignore_strategy_index = getPlayerIgnoreStrategyIndex(
213  ignore_strategy_name);
214 
215  if (ignore_strategy_index >= 0)
216  {
218  [ignore_strategy_index]);
219  }
220 
221  cfg->getList<std::pair<std::string, PlayerRelation *>,
222  std::map<std::string, PlayerRelation *> *>
223  ("player", &(mRelations), &player_conf_serialiser);
224 }
225 
226 
228 {
229  load();
230 
231  if (!mPersistIgnores)
232  {
233  clear(); // Yes, we still keep them around in the config file
234  // until the next update.
235  }
236 
238  (*it)->updateAll();
239 }
240 
242 {
243  serverConfig.setList<std::map<std::string,
244  PlayerRelation *>::const_iterator,
245  std::pair<std::string, PlayerRelation *>,
246  std::map<std::string, PlayerRelation *> *>
247  ("player", mRelations.begin(), mRelations.end(),
249 
255 
257 }
258 
259 void PlayerRelationsManager::signalUpdate(const std::string &name)
260 {
262  (*it)->updatedPlayer(name);
263 
264  if (actorManager != nullptr)
265  {
266  Being *const being = actorManager->findBeingByName(
267  name, ActorType::Player);
268 
269  if (being != nullptr &&
270  being->getType() == ActorType::Player)
271  {
272  being->updateColors();
273  }
274  }
275 }
276 
278  const std::string &player_name, const unsigned int flags) const
279 {
280  const std::map<std::string, PlayerRelation *>::const_iterator
281  it = mRelations.find(player_name);
282  if (it == mRelations.end())
283  {
284  return mDefaultPermissions & flags;
285  }
286 
287  const PlayerRelation *const r = (*it).second;
288  unsigned int permissions = PlayerRelation::RELATION_PERMISSIONS[
289  CAST_S32(r->mRelation)];
290 
291  switch (r->mRelation)
292  {
293  case Relation::NEUTRAL:
294  permissions = mDefaultPermissions;
295  break;
296 
297  case Relation::FRIEND:
298  permissions |= mDefaultPermissions; // widen
299  break;
300 
302  case Relation::IGNORED:
303  case Relation::ERASED:
305  case Relation::ENEMY2:
306  default:
307  permissions &= mDefaultPermissions; // narrow
308  break;
309  }
310 
311  return permissions & flags;
312 }
313 
315  const unsigned int flags) const
316 {
317  if (being == nullptr)
318  return false;
319 
320  if (being->getType() == ActorType::Player)
321  {
322  return static_cast<unsigned int>(hasPermission(
323  being->getName(), flags)) == flags;
324  }
325  return true;
326 }
327 
328 bool PlayerRelationsManager::hasPermission(const std::string &name,
329  const unsigned int flags) const
330 {
331  if (actorManager == nullptr)
332  return false;
333 
334  const unsigned int rejections = flags
335  & ~checkPermissionSilently(name, flags);
336  const bool permitted = (rejections == 0);
337 
338  if (!permitted)
339  {
340  // execute `ignore' strategy, if possible
341  if (mIgnoreStrategy != nullptr)
342  {
343  Being *const b = actorManager->findBeingByName(
344  name, ActorType::Player);
345 
346  if ((b != nullptr) && b->getType() == ActorType::Player)
347  mIgnoreStrategy->ignore(b, rejections);
348  }
349  }
350 
351  return permitted;
352 }
353 
354 void PlayerRelationsManager::setRelation(const std::string &player_name,
355  const RelationT relation)
356 {
357  if (localPlayer == nullptr ||
358  (relation != Relation::NEUTRAL &&
359  localPlayer->getName() == player_name))
360  {
361  return;
362  }
363 
364  PlayerRelation *const r = mRelations[player_name];
365  if (r == nullptr)
366  mRelations[player_name] = new PlayerRelation(relation);
367  else
368  r->mRelation = relation;
369 
370  signalUpdate(player_name);
371  store();
372 }
373 
375 {
376  StringVect *const retval = new StringVect;
377 
379  {
380  if (it->second != nullptr)
381  retval->push_back(it->first);
382  }
383 
384  std::sort(retval->begin(), retval->end(), playersRelSorter);
385 
386  return retval;
387 }
388 
390  const RelationT rel) const
391 {
392  StringVect *const retval = new StringVect;
393 
395  {
396  if ((it->second != nullptr) &&
397  it->second->mRelation == rel)
398  {
399  retval->push_back(it->first);
400  }
401  }
402 
403  std::sort(retval->begin(), retval->end(), playersRelSorter);
404 
405  return retval;
406 }
407 
408 void PlayerRelationsManager::removePlayer(const std::string &name)
409 {
410  delete mRelations[name];
411  mRelations.erase(name);
412  signalUpdate(name);
413 }
414 
415 
417  const std::string &name) const
418 {
419  const std::map<std::string, PlayerRelation *>::const_iterator
420  it = mRelations.find(name);
421  if (it != mRelations.end())
422  return (*it).second->mRelation;
423 
424  return Relation::NEUTRAL;
425 }
426 
428 // defaults
429 
431 {
432  return mDefaultPermissions;
433 }
434 
435 void PlayerRelationsManager::setDefault(const unsigned int permissions)
436 {
437  mDefaultPermissions = permissions;
438 
439  store();
440  signalUpdate("");
441 }
442 
443 void PlayerRelationsManager::ignoreTrade(const std::string &name) const
444 {
445  if (name.empty())
446  return;
447 
448  const RelationT relation = getRelation(name);
449 
450  if (relation == Relation::IGNORED ||
451  relation == Relation::DISREGARDED ||
452  relation == Relation::BLACKLISTED ||
453  relation == Relation::ERASED)
454  {
455  return;
456  }
458 }
459 
460 bool PlayerRelationsManager::checkBadRelation(const std::string &name) const
461 {
462  if (name.empty())
463  return true;
464 
465  const RelationT relation = getRelation(name);
466 
467  if (relation == Relation::IGNORED ||
468  relation == Relation::DISREGARDED ||
469  relation == Relation::BLACKLISTED ||
470  relation == Relation::ERASED ||
471  relation == Relation::ENEMY2)
472  {
473  return true;
474  }
475  return false;
476 }
477 
479 // ignore strategies
480 
481 
483 {
484  public:
487  {
488  // TRANSLATORS: ignore/unignore action
489  mDescription = _("Completely ignore");
491  }
492 
494 
495  void ignore(Being *const being A_UNUSED,
496  const unsigned int flags A_UNUSED) const override final
497  {
498  }
499 };
500 
502 {
503  public:
506  {
507  // TRANSLATORS: ignore/unignore action
508  mDescription = _("Print '...'");
509  mShortName = "dotdotdot";
510  }
511 
513 
514  void ignore(Being *const being,
515  const unsigned int flags A_UNUSED) const override final
516  {
517  if (being == nullptr)
518  return;
519 
520  logger->log("ignoring: " + being->getName());
521  being->setSpeech("...");
522  }
523 };
524 
525 
527 {
528  public:
531  {
532  // TRANSLATORS: ignore/unignore action
533  mDescription = _("Blink name");
534  mShortName = "blinkname";
535  }
536 
538 
539  void ignore(Being *const being,
540  const unsigned int flags A_UNUSED) const override final
541  {
542  if (being == nullptr)
543  return;
544 
545  logger->log("ignoring: " + being->getName());
546  being->flashName(200);
547  }
548 };
549 
551 {
552  public:
553  PIS_emote(const uint8_t emote_nr,
554  const std::string &description,
555  const std::string &shortname) :
557  mEmotion(emote_nr)
558  {
559  mDescription = description;
560  mShortName = shortname;
561  }
562 
564 
565  void ignore(Being *const being,
566  const unsigned int flags A_UNUSED) const override final
567  {
568  if (being == nullptr)
569  return;
570 
571  being->setEmote(mEmotion, IGNORE_EMOTE_TIME);
572  }
573  uint8_t mEmotion;
574 };
575 
576 STD_VECTOR<PlayerIgnoreStrategy *> *
578 {
579  if (mIgnoreStrategies.empty())
580  {
581  // not initialised yet?
583  // TRANSLATORS: ignore strategi
584  _("Floating '...' bubble"),
587  // TRANSLATORS: ignore strategi
588  _("Floating bubble"),
589  "emote1"));
590  mIgnoreStrategies.push_back(new PIS_nothing);
591  mIgnoreStrategies.push_back(new PIS_dotdotdot);
592  mIgnoreStrategies.push_back(new PIS_blinkname);
593  }
594  return &mIgnoreStrategies;
595 }
596 
597 bool PlayerRelationsManager::isGoodName(const std::string &name) const
598 {
599  const size_t size = name.size();
600 
601  if (size < 3)
602  return true;
603 
604  const std::map<std::string, PlayerRelation *>::const_iterator
605  it = mRelations.find(name);
606  if (it != mRelations.end())
607  return true;
608 
609  return checkName(name);
610 }
611 
613 {
614  if (being == nullptr)
615  return false;
616  if (being->getGoodStatus() != -1)
617  return being->getGoodStatus() == 1;
618 
619  const std::string &name = being->getName();
620  const size_t size = name.size();
621 
622  if (size < 3)
623  return true;
624 
625  const std::map<std::string, PlayerRelation *>::const_iterator
626  it = mRelations.find(name);
627  if (it != mRelations.end())
628  return true;
629 
630  const bool status = checkName(name);
631  being->setGoodStatus(status ? 1 : 0);
632  return status;
633 }
634 
635 bool PlayerRelationsManager::checkName(const std::string &name)
636 {
637  const size_t size = name.size();
638  const std::string check = config.getStringValue("unsecureChars");
639  const std::string lastChar = name.substr(size - 1, 1);
640 
641  if (name.substr(0, 1) == " " ||
642  lastChar == " " ||
643  lastChar == "." ||
644  name.find(" ") != std::string::npos)
645  {
646  return false;
647  }
648  else if (check.empty())
649  {
650  return true;
651  }
652  else if (name.find_first_of(check) != std::string::npos)
653  {
654  return false;
655  }
656  else
657  {
658  return true;
659  }
660 }
661 
ActorManager * actorManager
#define CAST_S32
Definition: cast.h:30
Being * findBeingByName(const std::string &name, const ActorTypeT type) const
Definition: being.h:96
void updateColors()
Definition: being.cpp:2663
const std::string & getName() const
Definition: being.h:232
void setGoodStatus(const int n)
Definition: being.h:803
ActorTypeT getType() const
Definition: being.h:116
int getGoodStatus() const
Definition: being.h:800
std::string getValue(const std::string &key, const std::string &deflt) const
void setList(const std::string &name, IT begin, IT end, ConfigurationListManager< T, CONT > *manager)
CONT getList(const std::string &name, CONT empty, ConfigurationListManager< T, CONT > *manager)
std::string getStringValue(const std::string &key) const
void setValue(const std::string &key, const std::string &value)
void log(const char *const log_text,...)
Definition: logger.cpp:269
void ignore(Being *const being, const unsigned int flags) const
void ignore(Being *const being, const unsigned int flags) const
PIS_emote(const uint8_t emote_nr, const std::string &description, const std::string &shortname)
void ignore(Being *const being, const unsigned int flags) const
uint8_t mEmotion
void ignore(Being *const being, const unsigned int flags) const
virtual void ignore(Being *const being, const unsigned int flags) const =0
unsigned int mDefaultPermissions
StringVect * getPlayers() const
void setDefault(const unsigned int permissions)
void setPlayerIgnoreStrategy(PlayerIgnoreStrategy *const strategy)
bool isGoodName(Being *const being) const
std::vector< PlayerIgnoreStrategy * > mIgnoreStrategies
PlayerIgnoreStrategy * mIgnoreStrategy
StringVect * getPlayersByRelation(const RelationT rel) const
std::vector< PlayerIgnoreStrategy * > * getPlayerIgnoreStrategies()
unsigned int getDefault() const
static bool checkName(const std::string &name)
void ignoreTrade(const std::string &name) const
RelationT getRelation(const std::string &name) const
void signalUpdate(const std::string &name)
bool hasPermission(const Being *const being, const unsigned int flags) const
bool checkBadRelation(const std::string &name) const
int getPlayerIgnoreStrategyIndex(const std::string &shortname)
std::map< std::string, PlayerRelation * > mRelations
unsigned int checkPermissionSilently(const std::string &player_name, const unsigned int flags) const
void removePlayer(const std::string &name)
std::list< PlayerRelationsListener * > mListeners
void setRelation(const std::string &name, const RelationT relation)
std::map< std::string, PlayerRelation * > * readConfigItem(const ConfigurationObject *const cobj, std::map< std::string, PlayerRelation * > *const container) const
Configuration config
Configuration serverConfig
void delete_all(Container &c)
Definition: dtor.h:56
#define FOR_EACHP(type, iter, array)
Definition: foreach.h:31
#define FOR_EACH(type, iter, array)
Definition: foreach.h:25
#define _(s)
Definition: gettext.h:35
#define override
Definition: localconsts.h:47
#define final
Definition: localconsts.h:46
#define A_DELETE_COPY(func)
Definition: localconsts.h:53
#define nullptr
Definition: localconsts.h:45
#define A_UNUSED
Definition: localconsts.h:160
#define A_DEFAULT_COPY(func)
Definition: localconsts.h:41
LocalPlayer * localPlayer
Logger * logger
Definition: logger.cpp:89
std::string toLower(std::string const &s)
std::string toString(T const &value)
converts any type to a string
Definition: catch.hpp:1774
int size()
Definition: emotedb.cpp:306
@ DISREGARDED
Definition: relation.h:33
@ ENEMY2
Definition: relation.h:37
@ IGNORED
Definition: relation.h:34
@ FRIEND
Definition: relation.h:32
@ ERASED
Definition: relation.h:35
@ NEUTRAL
Definition: relation.h:31
@ BLACKLISTED
Definition: relation.h:36
class anonymous_namespace{playerrelations.cpp}::SortPlayersFunctor playersRelSorter
static const unsigned int FIRST_IGNORE_EMOTE
static const char *const DEFAULT_PERMISSIONS
PlayerRelations::const_iterator PlayerRelationsCIter
static PlayerConfSerialiser player_conf_serialiser
static const char *const PERSIST_IGNORE_LIST
static const unsigned int IGNORE_EMOTE_TIME
static const char *const PLAYER_IGNORE_STRATEGY_NOP
static const char *const RELATION
PlayerRelationListeners::const_iterator PlayerRelationListenersCIter
static const char *const DEFAULT_IGNORE_STRATEGY
static const char *const PLAYER_IGNORE_STRATEGY_EMOTE0
static const char *const NAME
std::list< PlayerRelationsListener * > PlayerRelationListeners
std::map< std::string, PlayerRelation * > PlayerRelations
static const char *const PLAYER_IGNORE_STRATEGY
PlayerRelationsManager playerRelations
Relation ::T RelationT
Definition: relation.h:39
StringVect::const_iterator StringVectCIter
Definition: stringvector.h:31
std::vector< std::string > StringVect
Definition: stringvector.h:29
static const unsigned int RELATION_PERMISSIONS[RELATIONS_NR]
RelationT mRelation